summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile4
-rw-r--r--drivers/acpi/Kconfig11
-rw-r--r--drivers/acpi/Makefile4
-rw-r--r--drivers/acpi/acpi_apd.c27
-rw-r--r--drivers/acpi/acpi_lpss.c17
-rw-r--r--drivers/acpi/acpi_platform.c20
-rw-r--r--drivers/acpi/acpi_processor.c104
-rw-r--r--drivers/acpi/acpi_watchdog.c123
-rw-r--r--drivers/acpi/acpica/Makefile1
-rw-r--r--drivers/acpi/acpica/acapps.h10
-rw-r--r--drivers/acpi/acpica/acdebug.h2
-rw-r--r--drivers/acpi/acpica/acevents.h3
-rw-r--r--drivers/acpi/acpica/acglobal.h2
-rw-r--r--drivers/acpi/acpica/aclocal.h2
-rw-r--r--drivers/acpi/acpica/acnamesp.h8
-rw-r--r--drivers/acpi/acpica/acparser.h2
-rw-r--r--drivers/acpi/acpica/actables.h12
-rw-r--r--drivers/acpi/acpica/acutils.h53
-rw-r--r--drivers/acpi/acpica/dbconvert.c7
-rw-r--r--drivers/acpi/acpica/dbexec.c62
-rw-r--r--drivers/acpi/acpica/dbfileio.c20
-rw-r--r--drivers/acpi/acpica/dbinput.c2
-rw-r--r--drivers/acpi/acpica/dbmethod.c132
-rw-r--r--drivers/acpi/acpica/dbobject.c4
-rw-r--r--drivers/acpi/acpica/dsmethod.c14
-rw-r--r--drivers/acpi/acpica/dsutils.c17
-rw-r--r--drivers/acpi/acpica/dswexec.c3
-rw-r--r--drivers/acpi/acpica/dswload2.c7
-rw-r--r--drivers/acpi/acpica/evgpe.c57
-rw-r--r--drivers/acpi/acpica/evgpeinit.c7
-rw-r--r--drivers/acpi/acpica/evrgnini.c3
-rw-r--r--drivers/acpi/acpica/evxfgpe.c43
-rw-r--r--drivers/acpi/acpica/exconcat.c2
-rw-r--r--drivers/acpi/acpica/exconfig.c141
-rw-r--r--drivers/acpi/acpica/exconvrt.c8
-rw-r--r--drivers/acpi/acpica/exmisc.c4
-rw-r--r--drivers/acpi/acpica/exoparg1.c23
-rw-r--r--drivers/acpi/acpica/exresop.c11
-rw-r--r--drivers/acpi/acpica/extrace.c25
-rw-r--r--drivers/acpi/acpica/exutils.c8
-rw-r--r--drivers/acpi/acpica/hwgpe.c23
-rw-r--r--drivers/acpi/acpica/nsaccess.c6
-rw-r--r--drivers/acpi/acpica/nsconvert.c1
-rw-r--r--drivers/acpi/acpica/nsdump.c2
-rw-r--r--drivers/acpi/acpica/nsload.c30
-rw-r--r--drivers/acpi/acpica/nsparse.c165
-rw-r--r--drivers/acpi/acpica/nsutils.c66
-rw-r--r--drivers/acpi/acpica/psparse.c6
-rw-r--r--drivers/acpi/acpica/psxface.c84
-rw-r--r--drivers/acpi/acpica/tbdata.c147
-rw-r--r--drivers/acpi/acpica/tbfadt.c136
-rw-r--r--drivers/acpi/acpica/tbfind.c12
-rw-r--r--drivers/acpi/acpica/tbinstal.c73
-rw-r--r--drivers/acpi/acpica/tbutils.c3
-rw-r--r--drivers/acpi/acpica/tbxface.c4
-rw-r--r--drivers/acpi/acpica/tbxfload.c66
-rw-r--r--drivers/acpi/acpica/tbxfroot.c5
-rw-r--r--drivers/acpi/acpica/utaddress.c8
-rw-r--r--drivers/acpi/acpica/utbuffer.c30
-rw-r--r--drivers/acpi/acpica/utdebug.c61
-rw-r--r--drivers/acpi/acpica/utdecode.c2
-rw-r--r--drivers/acpi/acpica/uthex.c46
-rw-r--r--drivers/acpi/acpica/utinit.c2
-rw-r--r--drivers/acpi/acpica/utnonansi.c357
-rw-r--r--drivers/acpi/acpica/utosi.c22
-rw-r--r--drivers/acpi/acpica/utpredef.c2
-rw-r--r--drivers/acpi/acpica/utprint.c105
-rw-r--r--drivers/acpi/acpica/utstrtoul64.c348
-rw-r--r--drivers/acpi/acpica/uttrack.c4
-rw-r--r--drivers/acpi/acpica/utxface.c2
-rw-r--r--drivers/acpi/acpica/utxfinit.c9
-rw-r--r--drivers/acpi/arm64/Kconfig6
-rw-r--r--drivers/acpi/arm64/Makefile1
-rw-r--r--drivers/acpi/arm64/iort.c368
-rw-r--r--drivers/acpi/battery.c10
-rw-r--r--drivers/acpi/bus.c9
-rw-r--r--drivers/acpi/button.c85
-rw-r--r--drivers/acpi/cppc_acpi.c664
-rw-r--r--drivers/acpi/ec.c468
-rw-r--r--drivers/acpi/internal.h17
-rw-r--r--drivers/acpi/ioapic.c46
-rw-r--r--drivers/acpi/nfit/core.c48
-rw-r--r--drivers/acpi/pci_irq.c10
-rw-r--r--drivers/acpi/pci_root.c12
-rw-r--r--drivers/acpi/processor_core.c133
-rw-r--r--drivers/acpi/processor_driver.c95
-rw-r--r--drivers/acpi/processor_throttling.c4
-rw-r--r--drivers/acpi/scan.c2
-rw-r--r--drivers/acpi/sleep.c6
-rw-r--r--drivers/acpi/spcr.c111
-rw-r--r--drivers/acpi/sysfs.c99
-rw-r--r--drivers/acpi/tables.c16
-rw-r--r--drivers/base/Kconfig10
-rw-r--r--drivers/base/attribute_container.c2
-rw-r--r--drivers/base/core.c40
-rw-r--r--drivers/base/cpu.c11
-rw-r--r--drivers/base/dd.c33
-rw-r--r--drivers/base/dma-coherent.c10
-rw-r--r--drivers/base/dma-mapping.c11
-rw-r--r--drivers/base/platform-msi.c3
-rw-r--r--drivers/base/platform.c18
-rw-r--r--drivers/base/power/domain.c430
-rw-r--r--drivers/base/power/opp/core.c10
-rw-r--r--drivers/base/power/opp/of.c14
-rw-r--r--drivers/base/regmap/internal.h5
-rw-r--r--drivers/base/regmap/regmap-debugfs.c16
-rw-r--r--drivers/base/regmap/regmap.c64
-rw-r--r--drivers/base/soc.c9
-rw-r--r--drivers/bluetooth/bcm203x.c4
-rw-r--r--drivers/bluetooth/hci_vhci.c16
-rw-r--r--drivers/bus/arm-cci.c45
-rw-r--r--drivers/bus/arm-ccn.c54
-rw-r--r--drivers/bus/mips_cdmm.c70
-rw-r--r--drivers/char/bfin-otp.c40
-rw-r--r--drivers/char/mem.c6
-rw-r--r--drivers/char/mwave/3780i.c64
-rw-r--r--drivers/char/mwave/3780i.h8
-rw-r--r--drivers/char/mwave/mwavedd.c42
-rw-r--r--drivers/char/mwave/mwavedd.h14
-rw-r--r--drivers/char/mwave/smapi.c4
-rw-r--r--drivers/char/mwave/smapi.h6
-rw-r--r--drivers/char/mwave/tp3780i.c52
-rw-r--r--drivers/char/ppdev.c25
-rw-r--r--drivers/char/snsc.c7
-rw-r--r--drivers/char/tile-srom.c28
-rw-r--r--drivers/char/ttyprintk.c69
-rw-r--r--drivers/char/xillybus/xillybus_core.c4
-rw-r--r--drivers/clk/Kconfig20
-rw-r--r--drivers/clk/Makefile5
-rw-r--r--drivers/clk/at91/clk-generated.c30
-rw-r--r--drivers/clk/at91/clk-h32mx.c8
-rw-r--r--drivers/clk/at91/clk-main.c88
-rw-r--r--drivers/clk/at91/clk-master.c21
-rw-r--r--drivers/clk/at91/clk-peripheral.c39
-rw-r--r--drivers/clk/at91/clk-pll.c21
-rw-r--r--drivers/clk/at91/clk-plldiv.c24
-rw-r--r--drivers/clk/at91/clk-programmable.c22
-rw-r--r--drivers/clk/at91/clk-slow.c375
-rw-r--r--drivers/clk/at91/clk-smd.c22
-rw-r--r--drivers/clk/at91/clk-system.c22
-rw-r--r--drivers/clk/at91/clk-usb.c69
-rw-r--r--drivers/clk/at91/clk-utmi.c22
-rw-r--r--drivers/clk/at91/sckc.c464
-rw-r--r--drivers/clk/at91/sckc.h22
-rw-r--r--drivers/clk/axis/clk-artpec6.c4
-rw-r--r--drivers/clk/bcm/Kconfig30
-rw-r--r--drivers/clk/bcm/Makefile8
-rw-r--r--drivers/clk/bcm/clk-bcm2835-aux.c28
-rw-r--r--drivers/clk/bcm/clk-bcm2835.c146
-rw-r--r--drivers/clk/bcm/clk-bcm53573-ilp.c148
-rw-r--r--drivers/clk/bcm/clk-kona-setup.c80
-rw-r--r--drivers/clk/bcm/clk-kona.c9
-rw-r--r--drivers/clk/bcm/clk-kona.h7
-rw-r--r--drivers/clk/berlin/berlin2-avpll.c12
-rw-r--r--drivers/clk/berlin/berlin2-avpll.h8
-rw-r--r--drivers/clk/berlin/berlin2-div.c4
-rw-r--r--drivers/clk/berlin/berlin2-div.h4
-rw-r--r--drivers/clk/berlin/berlin2-pll.c6
-rw-r--r--drivers/clk/berlin/berlin2-pll.h9
-rw-r--r--drivers/clk/berlin/bg2.c98
-rw-r--r--drivers/clk/berlin/bg2q.c39
-rw-r--r--drivers/clk/clk-asm9260.c31
-rw-r--r--drivers/clk/clk-axi-clkgen.c12
-rw-r--r--drivers/clk/clk-axm5516.c39
-rw-r--r--drivers/clk/clk-cdce706.c40
-rw-r--r--drivers/clk/clk-cdce925.c42
-rw-r--r--drivers/clk/clk-clps711x.c78
-rw-r--r--drivers/clk/clk-cs2000-cp.c16
-rw-r--r--drivers/clk/clk-divider.c2
-rw-r--r--drivers/clk/clk-efm32gg.c63
-rw-r--r--drivers/clk/clk-fixed-factor.c74
-rw-r--r--drivers/clk/clk-fixed-rate.c72
-rw-r--r--drivers/clk/clk-ls1x.c161
-rw-r--r--drivers/clk/clk-max-gen.c194
-rw-r--r--drivers/clk/clk-max-gen.h32
-rw-r--r--drivers/clk/clk-max77686.c262
-rw-r--r--drivers/clk/clk-max77802.c96
-rw-r--r--drivers/clk/clk-mb86s7x.c16
-rw-r--r--drivers/clk/clk-moxart.c22
-rw-r--r--drivers/clk/clk-nspire.c19
-rw-r--r--drivers/clk/clk-palmas.c12
-rw-r--r--drivers/clk/clk-pwm.c9
-rw-r--r--drivers/clk/clk-qoriq.c6
-rw-r--r--drivers/clk/clk-rk808.c44
-rw-r--r--drivers/clk/clk-scpi.c33
-rw-r--r--drivers/clk/clk-si514.c11
-rw-r--r--drivers/clk/clk-si5351.c71
-rw-r--r--drivers/clk/clk-si570.c13
-rw-r--r--drivers/clk/clk-twl6040.c87
-rw-r--r--drivers/clk/clk-vt8500.c22
-rw-r--r--drivers/clk/clk-wm831x.c21
-rw-r--r--drivers/clk/clk-xgene.c221
-rw-r--r--drivers/clk/clk.c49
-rw-r--r--drivers/clk/h8300/clk-div.c10
-rw-r--r--drivers/clk/h8300/clk-h8s2678.c12
-rw-r--r--drivers/clk/imx/clk-imx35.c32
-rw-r--r--drivers/clk/imx/clk-imx51-imx53.c20
-rw-r--r--drivers/clk/imx/clk-imx6q.c46
-rw-r--r--drivers/clk/imx/clk-imx7d.c117
-rw-r--r--drivers/clk/imx/clk.h9
-rw-r--r--drivers/clk/loongson1/Makefile3
-rw-r--r--drivers/clk/loongson1/clk-loongson1b.c122
-rw-r--r--drivers/clk/loongson1/clk-loongson1c.c97
-rw-r--r--drivers/clk/loongson1/clk.c43
-rw-r--r--drivers/clk/loongson1/clk.h19
-rw-r--r--drivers/clk/mediatek/Kconfig21
-rw-r--r--drivers/clk/mediatek/Makefile6
-rw-r--r--drivers/clk/mediatek/clk-gate.c2
-rw-r--r--drivers/clk/mediatek/clk-mt8173.c4
-rw-r--r--drivers/clk/mediatek/clk-mtk.c12
-rw-r--r--drivers/clk/mediatek/clk-pll.c2
-rw-r--r--drivers/clk/meson/Makefile4
-rw-r--r--drivers/clk/meson/clkc.h2
-rw-r--r--drivers/clk/meson/gxbb-aoclk.c191
-rw-r--r--drivers/clk/meson/gxbb.c177
-rw-r--r--drivers/clk/meson/gxbb.h13
-rw-r--r--drivers/clk/meson/meson8b.c (renamed from drivers/clk/meson/meson8b-clkc.c)299
-rw-r--r--drivers/clk/meson/meson8b.h151
-rw-r--r--drivers/clk/microchip/clk-core.c6
-rw-r--r--drivers/clk/microchip/clk-pic32mzda.c1
-rw-r--r--drivers/clk/mmp/clk-mmp2.c1
-rw-r--r--drivers/clk/mvebu/Kconfig3
-rw-r--r--drivers/clk/mvebu/Makefile3
-rw-r--r--drivers/clk/mvebu/armada-37xx-periph.c447
-rw-r--r--drivers/clk/mvebu/armada-37xx-tbg.c158
-rw-r--r--drivers/clk/mvebu/armada-37xx-xtal.c91
-rw-r--r--drivers/clk/mvebu/armada-39x.c2
-rw-r--r--drivers/clk/mvebu/cp110-system-controller.c31
-rw-r--r--drivers/clk/nxp/clk-lpc18xx-creg.c3
-rw-r--r--drivers/clk/nxp/clk-lpc32xx.c1
-rw-r--r--drivers/clk/qcom/Kconfig19
-rw-r--r--drivers/clk/qcom/Makefile5
-rw-r--r--drivers/clk/qcom/clk-regmap.c5
-rw-r--r--drivers/clk/qcom/clk-regmap.h3
-rw-r--r--drivers/clk/qcom/common.c58
-rw-r--r--drivers/clk/qcom/gcc-ipq4019.c1
-rw-r--r--drivers/clk/qcom/gcc-mdm9615.c1727
-rw-r--r--drivers/clk/qcom/gcc-msm8996.c17
-rw-r--r--drivers/clk/qcom/lcc-mdm9615.c580
-rw-r--r--drivers/clk/qcom/mmcc-msm8996.c18
-rw-r--r--drivers/clk/renesas/clk-mstp.c2
-rw-r--r--drivers/clk/renesas/clk-rz.c24
-rw-r--r--drivers/clk/renesas/r8a7795-cpg-mssr.c4
-rw-r--r--drivers/clk/renesas/r8a7796-cpg-mssr.c31
-rw-r--r--drivers/clk/rockchip/Makefile1
-rw-r--r--drivers/clk/rockchip/clk-ddr.c154
-rw-r--r--drivers/clk/rockchip/clk-pll.c4
-rw-r--r--drivers/clk/rockchip/clk-rk3399.c56
-rw-r--r--drivers/clk/rockchip/clk-rockchip.c7
-rw-r--r--drivers/clk/rockchip/clk.c11
-rw-r--r--drivers/clk/rockchip/clk.h35
-rw-r--r--drivers/clk/samsung/clk-exynos-audss.c84
-rw-r--r--drivers/clk/samsung/clk-exynos5260.c350
-rw-r--r--drivers/clk/samsung/clk-exynos5410.c61
-rw-r--r--drivers/clk/samsung/clk-exynos5420.c37
-rw-r--r--drivers/clk/samsung/clk-exynos5440.c9
-rw-r--r--drivers/clk/samsung/clk-pll.c154
-rw-r--r--drivers/clk/samsung/clk-pll.h2
-rw-r--r--drivers/clk/st/clk-flexgen.c63
-rw-r--r--drivers/clk/st/clkgen-fsyn.c481
-rw-r--r--drivers/clk/st/clkgen-mux.c748
-rw-r--r--drivers/clk/st/clkgen-pll.c472
-rw-r--r--drivers/clk/sunxi-ng/Kconfig39
-rw-r--r--drivers/clk/sunxi-ng/Makefile4
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun6i-a31.c1239
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun6i-a31.h72
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a23-a33.h63
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a23.c737
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-a33.c780
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun8i-h3.c10
-rw-r--r--drivers/clk/sunxi-ng/ccu_div.h66
-rw-r--r--drivers/clk/sunxi-ng/ccu_mp.c23
-rw-r--r--drivers/clk/sunxi-ng/ccu_mp.h2
-rw-r--r--drivers/clk/sunxi-ng/ccu_mult.c133
-rw-r--r--drivers/clk/sunxi-ng/ccu_mult.h35
-rw-r--r--drivers/clk/sunxi-ng/ccu_mux.c56
-rw-r--r--drivers/clk/sunxi-ng/ccu_mux.h68
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkm.c44
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkm.h23
-rw-r--r--drivers/clk/sunxi-ng/ccu_nkmp.c21
-rw-r--r--drivers/clk/sunxi-ng/ccu_nm.c16
-rw-r--r--drivers/clk/sunxi/clk-mod0.c3
-rw-r--r--drivers/clk/sunxi/clk-sun8i-apb0.c4
-rw-r--r--drivers/clk/uniphier/Kconfig9
-rw-r--r--drivers/clk/uniphier/Makefile8
-rw-r--r--drivers/clk/uniphier/clk-uniphier-core.c214
-rw-r--r--drivers/clk/uniphier/clk-uniphier-fixed-factor.c48
-rw-r--r--drivers/clk/uniphier/clk-uniphier-fixed-rate.c47
-rw-r--r--drivers/clk/uniphier/clk-uniphier-gate.c97
-rw-r--r--drivers/clk/uniphier/clk-uniphier-mio.c101
-rw-r--r--drivers/clk/uniphier/clk-uniphier-mux.c95
-rw-r--r--drivers/clk/uniphier/clk-uniphier-peri.c57
-rw-r--r--drivers/clk/uniphier/clk-uniphier-sys.c151
-rw-r--r--drivers/clk/uniphier/clk-uniphier.h122
-rw-r--r--drivers/clk/versatile/clk-icst.c310
-rw-r--r--drivers/clk/zte/Makefile1
-rw-r--r--drivers/clk/zte/clk-zx296718.c924
-rw-r--r--drivers/clk/zte/clk.c21
-rw-r--r--drivers/clk/zte/clk.h129
-rw-r--r--drivers/clocksource/Kconfig10
-rw-r--r--drivers/clocksource/arm_arch_timer.c113
-rw-r--r--drivers/clocksource/mips-gic-timer.c7
-rw-r--r--drivers/clocksource/moxart_timer.c193
-rw-r--r--drivers/clocksource/timer-atmel-pit.c96
-rw-r--r--drivers/clocksource/timer-oxnas-rps.c2
-rw-r--r--drivers/clocksource/timer-ti-32k.c2
-rw-r--r--drivers/cpufreq/Kconfig7
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c68
-rw-r--r--drivers/cpufreq/cpufreq-dt-platdev.c11
-rw-r--r--drivers/cpufreq/cpufreq-dt.c6
-rw-r--r--drivers/cpufreq/cpufreq-dt.h19
-rw-r--r--drivers/cpufreq/cpufreq.c132
-rw-r--r--drivers/cpufreq/cpufreq_governor.c2
-rw-r--r--drivers/cpufreq/intel_pstate.c71
-rw-r--r--drivers/cpufreq/kirkwood-cpufreq.c8
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c1
-rw-r--r--drivers/cpufreq/sti-cpufreq.c2
-rw-r--r--drivers/cpuidle/coupled.c75
-rw-r--r--drivers/cpuidle/cpuidle-arm.c1
-rw-r--r--drivers/cpuidle/cpuidle-powernv.c51
-rw-r--r--drivers/cpuidle/cpuidle-pseries.c51
-rw-r--r--drivers/devfreq/Kconfig30
-rw-r--r--drivers/devfreq/Makefile1
-rw-r--r--drivers/devfreq/event/Kconfig11
-rw-r--r--drivers/devfreq/event/Makefile1
-rw-r--r--drivers/devfreq/event/exynos-ppmu.c2
-rw-r--r--drivers/devfreq/event/rockchip-dfi.c256
-rw-r--r--drivers/devfreq/rk3399_dmc.c470
-rw-r--r--drivers/dma/dw/core.c39
-rw-r--r--drivers/dma/dw/regs.h5
-rw-r--r--drivers/dma/hsu/hsu.c9
-rw-r--r--drivers/dma/hsu/pci.c6
-rw-r--r--drivers/dma/imx-sdma.c56
-rw-r--r--drivers/edac/mce_amd.c244
-rw-r--r--drivers/extcon/Kconfig6
-rw-r--r--drivers/extcon/Makefile1
-rw-r--r--drivers/extcon/extcon-adc-jack.c27
-rw-r--r--drivers/extcon/extcon-arizona.c27
-rw-r--r--drivers/extcon/extcon-axp288.c8
-rw-r--r--drivers/extcon/extcon-gpio.c3
-rw-r--r--drivers/extcon/extcon-max14577.c18
-rw-r--r--drivers/extcon/extcon-max3355.c8
-rw-r--r--drivers/extcon/extcon-max77693.c46
-rw-r--r--drivers/extcon/extcon-max77843.c22
-rw-r--r--drivers/extcon/extcon-max8997.c20
-rw-r--r--drivers/extcon/extcon-palmas.c16
-rw-r--r--drivers/extcon/extcon-qcom-spmi-misc.c170
-rw-r--r--drivers/extcon/extcon-rt8973a.c4
-rw-r--r--drivers/extcon/extcon-sm5502.c4
-rw-r--r--drivers/extcon/extcon-usb-gpio.c8
-rw-r--r--drivers/extcon/extcon.c774
-rw-r--r--drivers/firmware/dcdbas.c51
-rw-r--r--drivers/firmware/efi/Kconfig17
-rw-r--r--drivers/firmware/efi/Makefile3
-rw-r--r--drivers/firmware/efi/arm-init.c50
-rw-r--r--drivers/firmware/efi/arm-runtime.c26
-rw-r--r--drivers/firmware/efi/efi-pstore.c36
-rw-r--r--drivers/firmware/efi/efi.c67
-rw-r--r--drivers/firmware/efi/efivars.c22
-rw-r--r--drivers/firmware/efi/esrt.c23
-rw-r--r--drivers/firmware/efi/fake_mem.c125
-rw-r--r--drivers/firmware/efi/memmap.c303
-rw-r--r--drivers/firmware/efi/runtime-map.c35
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c81
-rw-r--r--drivers/firmware/efi/test/Makefile1
-rw-r--r--drivers/firmware/efi/test/efi_test.c749
-rw-r--r--drivers/firmware/efi/test/efi_test.h110
-rw-r--r--drivers/firmware/efi/vars.c142
-rw-r--r--drivers/firmware/google/gsmi.c3
-rw-r--r--drivers/fpga/Kconfig1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c2
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/device.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c3
-rw-r--r--drivers/gpu/drm/radeon/si_dpm.c6
-rw-r--r--drivers/gpu/drm/udl/udl_fb.c2
-rw-r--r--drivers/hid/uhid.c13
-rw-r--r--drivers/hv/channel.c148
-rw-r--r--drivers/hv/channel_mgmt.c130
-rw-r--r--drivers/hv/connection.c8
-rw-r--r--drivers/hv/hv.c8
-rw-r--r--drivers/hv/hv_balloon.c254
-rw-r--r--drivers/hv/hv_fcopy.c14
-rw-r--r--drivers/hv/hv_kvp.c27
-rw-r--r--drivers/hv/hv_snapshot.c109
-rw-r--r--drivers/hv/hv_util.c155
-rw-r--r--drivers/hv/hv_utils_transport.c15
-rw-r--r--drivers/hv/hv_utils_transport.h4
-rw-r--r--drivers/hv/hyperv_vmbus.h9
-rw-r--r--drivers/hv/ring_buffer.c76
-rw-r--r--drivers/hv/vmbus_drv.c16
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/adt7411.c51
-rw-r--r--drivers/hwmon/adt7470.c108
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c36
-rw-r--r--drivers/hwmon/ftsteutates.c45
-rw-r--r--drivers/hwmon/hwmon.c616
-rw-r--r--drivers/hwmon/ibmpowernv.c8
-rw-r--r--drivers/hwmon/iio_hwmon.c5
-rw-r--r--drivers/hwmon/it87.c8
-rw-r--r--drivers/hwmon/jc42.c293
-rw-r--r--drivers/hwmon/lm75.c185
-rw-r--r--drivers/hwmon/lm90.c811
-rw-r--r--drivers/hwmon/lm95241.c469
-rw-r--r--drivers/hwmon/lm95245.c638
-rw-r--r--drivers/hwmon/ltc4151.c22
-rw-r--r--drivers/hwmon/ltc4245.c362
-rw-r--r--drivers/hwmon/max31790.c521
-rw-r--r--drivers/hwmon/max6650.c155
-rw-r--r--drivers/hwmon/nct6775.c23
-rw-r--r--drivers/hwmon/nct7904.c555
-rw-r--r--drivers/hwmon/ntc_thermistor.c107
-rw-r--r--drivers/hwmon/pmbus/Kconfig6
-rw-r--r--drivers/hwmon/pmbus/pmbus.c20
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c3
-rw-r--r--drivers/hwmon/scpi-hwmon.c1
-rw-r--r--drivers/hwmon/tmp102.c154
-rw-r--r--drivers/hwmon/tmp421.c133
-rw-r--r--drivers/hwmon/xgene-hwmon.c787
-rw-r--r--drivers/hwtracing/coresight/coresight-etb10.c28
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.c157
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.h34
-rw-r--r--drivers/hwtracing/coresight/coresight-etm.h8
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x-sysfs.c3
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x.c39
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-sysfs.c63
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.c419
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.h11
-rw-r--r--drivers/hwtracing/coresight/coresight-funnel.c20
-rw-r--r--drivers/hwtracing/coresight/coresight-priv.h23
-rw-r--r--drivers/hwtracing/coresight/coresight-replicator-qcom.c18
-rw-r--r--drivers/hwtracing/coresight/coresight-replicator.c20
-rw-r--r--drivers/hwtracing/coresight/coresight-stm.c53
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-etf.c6
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-etr.c14
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.c80
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.h4
-rw-r--r--drivers/hwtracing/coresight/coresight-tpiu.c18
-rw-r--r--drivers/hwtracing/coresight/coresight.c31
-rw-r--r--drivers/hwtracing/coresight/of_coresight.c3
-rw-r--r--drivers/i2c/busses/i2c-i801.c4
-rw-r--r--drivers/iio/pressure/Kconfig2
-rw-r--r--drivers/input/joydev.c6
-rw-r--r--drivers/input/misc/uinput.c15
-rw-r--r--drivers/iommu/amd_iommu.c51
-rw-r--r--drivers/iommu/amd_iommu_types.h2
-rw-r--r--drivers/irqchip/Kconfig15
-rw-r--r--drivers/irqchip/Makefile3
-rw-r--r--drivers/irqchip/irq-gic-pm.c23
-rw-r--r--drivers/irqchip/irq-gic-v3-its-pci-msi.c88
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c171
-rw-r--r--drivers/irqchip/irq-gic-v3.c15
-rw-r--r--drivers/irqchip/irq-gic.c38
-rw-r--r--drivers/irqchip/irq-jcore-aic.c95
-rw-r--r--drivers/irqchip/irq-keystone.c2
-rw-r--r--drivers/irqchip/irq-mips-gic.c14
-rw-r--r--drivers/irqchip/irq-mvebu-pic.c197
-rw-r--r--drivers/irqchip/irq-stm32-exti.c201
-rw-r--r--drivers/leds/Kconfig28
-rw-r--r--drivers/leds/Makefile3
-rw-r--r--drivers/leds/led-triggers.c25
-rw-r--r--drivers/leds/leds-gpio.c78
-rw-r--r--drivers/leds/leds-is31fl319x.c450
-rw-r--r--drivers/leds/leds-mlxcpld.c430
-rw-r--r--drivers/leds/leds-pm8058.c191
-rw-r--r--drivers/lightnvm/core.c19
-rw-r--r--drivers/mailbox/pcc.c316
-rw-r--r--drivers/mcb/Kconfig9
-rw-r--r--drivers/mcb/Makefile1
-rw-r--r--drivers/mcb/mcb-core.c18
-rw-r--r--drivers/mcb/mcb-internal.h9
-rw-r--r--drivers/mcb/mcb-lpc.c158
-rw-r--r--drivers/mcb/mcb-parse.c126
-rw-r--r--drivers/mcb/mcb-pci.c1
-rw-r--r--drivers/md/raid5.c84
-rw-r--r--drivers/md/raid5.h4
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.c1
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c1
-rw-r--r--drivers/media/rc/imon.c13
-rw-r--r--drivers/media/rc/redrat3.c4
-rw-r--r--drivers/media/usb/airspy/airspy.c1
-rw-r--r--drivers/media/usb/as102/as102_usb_drv.c2
-rw-r--r--drivers/media/usb/au0828/au0828-video.c1
-rw-r--r--drivers/media/usb/cpia2/cpia2_usb.c1
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-audio.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c4
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.c2
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c4
-rw-r--r--drivers/media/usb/em28xx/em28xx-audio.c1
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c1
-rw-r--r--drivers/media/usb/gspca/benq.c4
-rw-r--r--drivers/media/usb/gspca/gspca.c4
-rw-r--r--drivers/media/usb/gspca/konica.c4
-rw-r--r--drivers/media/usb/hackrf/hackrf.c1
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-video.c4
-rw-r--r--drivers/media/usb/msi2500/msi2500.c1
-rw-r--r--drivers/media/usb/pwc/pwc-if.c1
-rw-r--r--drivers/media/usb/s2255/s2255drv.c9
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c4
-rw-r--r--drivers/media/usb/stkwebcam/stk-webcam.c4
-rw-r--r--drivers/media/usb/tm6000/tm6000-dvb.c4
-rw-r--r--drivers/media/usb/tm6000/tm6000-video.c1
-rw-r--r--drivers/media/usb/usbvision/usbvision-core.c5
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c4
-rw-r--r--drivers/memory/of_memory.c1
-rw-r--r--drivers/mfd/lpc_ich.c4
-rw-r--r--drivers/mfd/tps65218.c9
-rw-r--r--drivers/misc/Kconfig28
-rw-r--r--drivers/misc/Makefile3
-rw-r--r--drivers/misc/bmp085-i2c.c83
-rw-r--r--drivers/misc/bmp085-spi.c79
-rw-r--r--drivers/misc/bmp085.c506
-rw-r--r--drivers/misc/bmp085.h33
-rw-r--r--drivers/misc/eeprom/at25.c20
-rw-r--r--drivers/misc/genwqe/card_base.c15
-rw-r--r--drivers/misc/genwqe/card_ddcb.c2
-rw-r--r--drivers/misc/genwqe/card_utils.c4
-rw-r--r--drivers/misc/hpilo.c17
-rw-r--r--drivers/misc/mei/amthif.c345
-rw-r--r--drivers/misc/mei/bus.c11
-rw-r--r--drivers/misc/mei/client.c190
-rw-r--r--drivers/misc/mei/client.h7
-rw-r--r--drivers/misc/mei/hbm.c49
-rw-r--r--drivers/misc/mei/hw-me-regs.h3
-rw-r--r--drivers/misc/mei/hw-me.c3
-rw-r--r--drivers/misc/mei/hw-txe.c3
-rw-r--r--drivers/misc/mei/init.c2
-rw-r--r--drivers/misc/mei/interrupt.c77
-rw-r--r--drivers/misc/mei/main.c58
-rw-r--r--drivers/misc/mei/mei_dev.h31
-rw-r--r--drivers/misc/mei/pci-me.c11
-rw-r--r--drivers/misc/mei/pci-txe.c7
-rw-r--r--drivers/misc/mic/scif/scif_dma.c6
-rw-r--r--drivers/misc/mic/scif/scif_mmap.c4
-rw-r--r--drivers/misc/pch_phub.c20
-rw-r--r--drivers/misc/vmw_vmci/vmci_host.c14
-rw-r--r--drivers/mmc/host/vub300.c2
-rw-r--r--drivers/mtd/nand/davinci_nand.c3
-rw-r--r--drivers/mtd/nand/omap2.c2
-rw-r--r--drivers/net/can/dev.c27
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c45
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.h1
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c24
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c10
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c15
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c232
-rw-r--r--drivers/net/virtio_net.c110
-rw-r--r--drivers/net/wireless/ath/carl9170/debug.c6
-rw-r--r--drivers/net/wireless/broadcom/b43/debugfs.c6
-rw-r--r--drivers/net/wireless/broadcom/b43legacy/debugfs.c6
-rw-r--r--drivers/nvdimm/core.c8
-rw-r--r--drivers/nvdimm/nd.h22
-rw-r--r--drivers/nvdimm/region_devs.c22
-rw-r--r--drivers/nvmem/rockchip-efuse.c133
-rw-r--r--drivers/of/fdt.c11
-rw-r--r--drivers/of/of_numa.c66
-rw-r--r--drivers/oprofile/timer_int.c44
-rw-r--r--drivers/pci/msi.c172
-rw-r--r--drivers/pci/pci-mid.c7
-rw-r--r--drivers/pci/pci.c24
-rw-r--r--drivers/pci/setup-bus.c8
-rw-r--r--drivers/perf/arm_pmu.c78
-rw-r--r--drivers/phy/Kconfig36
-rw-r--r--drivers/phy/Makefile4
-rw-r--r--drivers/phy/phy-bcm-ns-usb3.c274
-rw-r--r--drivers/phy/phy-bcm-ns2-pcie.c28
-rw-r--r--drivers/phy/phy-core.c15
-rw-r--r--drivers/phy/phy-da8xx-usb.c2
-rw-r--r--drivers/phy/phy-exynos5-usbdrd.c4
-rw-r--r--drivers/phy/phy-omap-usb2.c100
-rw-r--r--drivers/phy/phy-qcom-ufs.c6
-rw-r--r--drivers/phy/phy-rcar-gen3-usb2.c1
-rw-r--r--drivers/phy/phy-rockchip-inno-usb2.c707
-rw-r--r--drivers/phy/phy-rockchip-pcie.c357
-rw-r--r--drivers/phy/phy-rockchip-typec.c1023
-rw-r--r--drivers/phy/phy-rockchip-usb.c20
-rw-r--r--drivers/phy/phy-sun4i-usb.c154
-rw-r--r--drivers/phy/phy-twl4030-usb.c25
-rw-r--r--drivers/phy/tegra/xusb.c4
-rw-r--r--drivers/platform/x86/intel_pmc_ipc.c12
-rw-r--r--drivers/pnp/isapnp/core.c5
-rw-r--r--drivers/power/avs/smartreflex.c115
-rw-r--r--drivers/powercap/intel_rapl.c4
-rw-r--r--drivers/regulator/Kconfig10
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/core.c154
-rw-r--r--drivers/regulator/dbx500-prcmu.c18
-rw-r--r--drivers/regulator/devres.c7
-rw-r--r--drivers/regulator/hi6421-regulator.c3
-rw-r--r--drivers/regulator/ltc3676.c420
-rw-r--r--drivers/regulator/pv88080-regulator.c263
-rw-r--r--drivers/regulator/pv88080-regulator.h114
-rw-r--r--drivers/regulator/pwm-regulator.c10
-rw-r--r--drivers/regulator/rk808-regulator.c3
-rw-r--r--drivers/regulator/tps65218-regulator.c8
-rw-r--r--drivers/regulator/tps65910-regulator.c6
-rw-r--r--drivers/s390/char/sclp_ctl.c19
-rw-r--r--drivers/scsi/hosts.c2
-rw-r--r--drivers/scsi/scsi.c1
-rw-r--r--drivers/scsi/scsi_priv.h1
-rw-r--r--drivers/scsi/virtio_scsi.c76
-rw-r--r--drivers/soc/samsung/pm_domains.c23
-rw-r--r--drivers/spmi/spmi-pmic-arb.c1
-rw-r--r--drivers/staging/board/board.c9
-rw-r--r--drivers/staging/fsl-mc/bus/mc-msi.c3
-rw-r--r--drivers/tty/pty.c2
-rw-r--r--drivers/tty/serial/8250/8250.h5
-rw-r--r--drivers/tty/serial/8250/8250_core.c2
-rw-r--r--drivers/tty/serial/8250/8250_dma.c14
-rw-r--r--drivers/tty/serial/8250/8250_dw.c63
-rw-r--r--drivers/tty/serial/8250/8250_lpss.c378
-rw-r--r--drivers/tty/serial/8250/8250_mid.c8
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c6
-rw-r--r--drivers/tty/serial/8250/8250_of.c7
-rw-r--r--drivers/tty/serial/8250/8250_pci.c312
-rw-r--r--drivers/tty/serial/8250/8250_port.c81
-rw-r--r--drivers/tty/serial/8250/Kconfig16
-rw-r--r--drivers/tty/serial/8250/Makefile1
-rw-r--r--drivers/tty/serial/Kconfig2
-rw-r--r--drivers/tty/serial/altera_jtaguart.c2
-rw-r--r--drivers/tty/serial/altera_uart.c2
-rw-r--r--drivers/tty/serial/amba-pl011.c57
-rw-r--r--drivers/tty/serial/arc_uart.c2
-rw-r--r--drivers/tty/serial/atmel_serial.c58
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c2
-rw-r--r--drivers/tty/serial/earlycon-arm-semihost.c3
-rw-r--r--drivers/tty/serial/earlycon.c26
-rw-r--r--drivers/tty/serial/fsl_lpuart.c814
-rw-r--r--drivers/tty/serial/imx.c213
-rw-r--r--drivers/tty/serial/jsm/jsm_tty.c2
-rw-r--r--drivers/tty/serial/max3100.c2
-rw-r--r--drivers/tty/serial/max310x.c2
-rw-r--r--drivers/tty/serial/men_z135_uart.c2
-rw-r--r--drivers/tty/serial/mxs-auart.c15
-rw-r--r--drivers/tty/serial/pch_uart.c7
-rw-r--r--drivers/tty/serial/samsung.c2
-rw-r--r--drivers/tty/serial/samsung.h2
-rw-r--r--drivers/tty/serial/sc16is7xx.c20
-rw-r--r--drivers/tty/serial/serial_core.c136
-rw-r--r--drivers/tty/serial/sh-sci.c2
-rw-r--r--drivers/tty/serial/st-asc.c2
-rw-r--r--drivers/tty/serial/stm32-usart.c613
-rw-r--r--drivers/tty/serial/stm32-usart.h229
-rw-r--r--drivers/tty/serial/timbuart.c2
-rw-r--r--drivers/tty/serial/uartlite.c2
-rw-r--r--drivers/tty/serial/vt8500_serial.c8
-rw-r--r--drivers/tty/serial/xilinx_uartps.c304
-rw-r--r--drivers/tty/vt/vt.c13
-rw-r--r--drivers/uio/uio_dmem_genirq.c2
-rw-r--r--drivers/usb/Kconfig24
-rw-r--r--drivers/usb/atm/cxacru.c6
-rw-r--r--drivers/usb/atm/speedtch.c1
-rw-r--r--drivers/usb/atm/ueagle-atm.c13
-rw-r--r--drivers/usb/atm/usbatm.c8
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c3
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.h1
-rw-r--r--drivers/usb/chipidea/host.c3
-rw-r--r--drivers/usb/chipidea/udc.c42
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c22
-rw-r--r--drivers/usb/class/cdc-acm.c49
-rw-r--r--drivers/usb/class/cdc-wdm.c165
-rw-r--r--drivers/usb/class/usbtmc.c11
-rw-r--r--drivers/usb/common/ulpi.c16
-rw-r--r--drivers/usb/core/Kconfig25
-rw-r--r--drivers/usb/core/Makefile5
-rw-r--r--drivers/usb/core/hcd.c7
-rw-r--r--drivers/usb/core/hub.c11
-rw-r--r--drivers/usb/core/ledtrig-usbport.c314
-rw-r--r--drivers/usb/core/message.c10
-rw-r--r--drivers/usb/core/of.c1
-rw-r--r--drivers/usb/core/otg_whitelist.h2
-rw-r--r--drivers/usb/core/urb.c4
-rw-r--r--drivers/usb/core/usb.c11
-rw-r--r--drivers/usb/dwc2/core.c126
-rw-r--r--drivers/usb/dwc2/core.h8
-rw-r--r--drivers/usb/dwc2/gadget.c94
-rw-r--r--drivers/usb/dwc2/hcd.c2
-rw-r--r--drivers/usb/dwc2/hw.h1
-rw-r--r--drivers/usb/dwc3/Kconfig2
-rw-r--r--drivers/usb/dwc3/core.c122
-rw-r--r--drivers/usb/dwc3/core.h35
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c48
-rw-r--r--drivers/usb/dwc3/gadget.c278
-rw-r--r--drivers/usb/dwc3/ulpi.c10
-rw-r--r--drivers/usb/gadget/Kconfig39
-rw-r--r--drivers/usb/gadget/composite.c16
-rw-r--r--drivers/usb/gadget/configfs.c5
-rw-r--r--drivers/usb/gadget/function/f_fs.c45
-rw-r--r--drivers/usb/gadget/function/f_hid.c28
-rw-r--r--drivers/usb/gadget/function/f_loopback.c14
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c28
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h1
-rw-r--r--drivers/usb/gadget/function/f_midi.c234
-rw-r--r--drivers/usb/gadget/function/f_ncm.c84
-rw-r--r--drivers/usb/gadget/function/f_printer.c6
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c6
-rw-r--r--drivers/usb/gadget/function/f_uvc.c7
-rw-r--r--drivers/usb/gadget/function/storage_common.c24
-rw-r--r--drivers/usb/gadget/function/storage_common.h10
-rw-r--r--drivers/usb/gadget/function/u_ether.c8
-rw-r--r--drivers/usb/gadget/function/u_ether.h1
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c2
-rw-r--r--drivers/usb/gadget/u_f.c6
-rw-r--r--drivers/usb/gadget/u_f.h17
-rw-r--r--drivers/usb/gadget/udc/core.c4
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c16
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c5
-rw-r--r--drivers/usb/gadget/udc/net2280.c80
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c2
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c51
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c4
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/usb/host/bcma-hcd.c169
-rw-r--r--drivers/usb/host/ehci-platform.c2
-rw-r--r--drivers/usb/host/fhci-hcd.c4
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c18
-rw-r--r--drivers/usb/host/max3421-hcd.c8
-rw-r--r--drivers/usb/host/ohci-at91.c69
-rw-r--r--drivers/usb/host/ohci-omap.c1
-rw-r--r--drivers/usb/host/ohci-sa1111.c4
-rw-r--r--drivers/usb/host/uhci-hcd.c5
-rw-r--r--drivers/usb/host/whci/init.c2
-rw-r--r--drivers/usb/host/xhci-tegra.c2
-rw-r--r--drivers/usb/host/xhci.c4
-rw-r--r--drivers/usb/misc/Kconfig6
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/adutux.c21
-rw-r--r--drivers/usb/misc/appledisplay.c17
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c5
-rw-r--r--drivers/usb/misc/cytherm.c32
-rw-r--r--drivers/usb/misc/ezusb.c2
-rw-r--r--drivers/usb/misc/ftdi-elan.c67
-rw-r--r--drivers/usb/misc/idmouse.c1
-rw-r--r--drivers/usb/misc/iowarrior.c26
-rw-r--r--drivers/usb/misc/ldusb.c28
-rw-r--r--drivers/usb/misc/legousbtower.c59
-rw-r--r--drivers/usb/misc/lvstest.c25
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c3
-rw-r--r--drivers/usb/misc/trancevibrator.c3
-rw-r--r--drivers/usb/misc/usb4604.c175
-rw-r--r--drivers/usb/misc/usblcd.c9
-rw-r--r--drivers/usb/misc/usbsevseg.c8
-rw-r--r--drivers/usb/misc/uss720.c5
-rw-r--r--drivers/usb/misc/yurex.c16
-rw-r--r--drivers/usb/musb/Kconfig2
-rw-r--r--drivers/usb/musb/am35x.c8
-rw-r--r--drivers/usb/musb/da8xx.c164
-rw-r--r--drivers/usb/musb/musb_core.c81
-rw-r--r--drivers/usb/musb/musb_core.h2
-rw-r--r--drivers/usb/musb/musb_dsps.c80
-rw-r--r--drivers/usb/musb/musb_gadget.c3
-rw-r--r--drivers/usb/musb/musb_virthub.c1
-rw-r--r--drivers/usb/musb/omap2430.c76
-rw-r--r--drivers/usb/musb/sunxi.c61
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c8
-rw-r--r--drivers/usb/phy/phy-generic.c9
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c61
-rw-r--r--drivers/usb/renesas_usbhs/common.c4
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c8
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c10
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c4
-rw-r--r--drivers/usb/serial/cp210x.c1
-rw-r--r--drivers/usb/serial/keyspan_pda.c4
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c171
-rw-r--r--drivers/usb/storage/alauda.c11
-rw-r--r--drivers/usb/storage/scsiglue.c8
-rw-r--r--drivers/usb/storage/sddr09.c14
-rw-r--r--drivers/usb/storage/unusual_devs.h7
-rw-r--r--drivers/usb/storage/usb.c16
-rw-r--r--drivers/usb/usb-skeleton.c14
-rw-r--r--drivers/usb/usbip/Kconfig24
-rw-r--r--drivers/usb/usbip/stub_rx.c1
-rw-r--r--drivers/usb/usbip/vhci.h54
-rw-r--r--drivers/usb/usbip/vhci_hcd.c285
-rw-r--r--drivers/usb/usbip/vhci_rx.c21
-rw-r--r--drivers/usb/usbip/vhci_sysfs.c296
-rw-r--r--drivers/usb/usbip/vudc_dev.c2
-rw-r--r--drivers/usb/usbip/vudc_rx.c2
-rw-r--r--drivers/usb/wusbcore/cbaf.c3
-rw-r--r--drivers/usb/wusbcore/crypto.c4
-rw-r--r--drivers/usb/wusbcore/security.c4
-rw-r--r--drivers/usb/wusbcore/wa-nep.c9
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c4
-rw-r--r--drivers/uwb/hwa-rc.c4
-rw-r--r--drivers/vhost/test.c13
-rw-r--r--drivers/vme/bridges/Kconfig8
-rw-r--r--drivers/vme/bridges/Makefile1
-rw-r--r--drivers/vme/bridges/vme_ca91cx42.c8
-rw-r--r--drivers/vme/bridges/vme_ca91cx42.h3
-rw-r--r--drivers/vme/bridges/vme_fake.c1306
-rw-r--r--drivers/vme/bridges/vme_tsi148.c8
-rw-r--r--drivers/vme/bridges/vme_tsi148.h3
-rw-r--r--drivers/vme/vme.c31
-rw-r--r--drivers/vme/vme_bridge.h3
-rw-r--r--drivers/w1/slaves/w1_therm.c22
-rw-r--r--drivers/w1/w1.c4
-rw-r--r--drivers/watchdog/Kconfig13
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/pcwd_usb.c4
-rw-r--r--drivers/watchdog/wdat_wdt.c526
803 files changed, 41016 insertions, 15182 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 53abb4a..f0afdfb 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -29,6 +29,8 @@ obj-$(CONFIG_SFI) += sfi/
# was used and do nothing if so
obj-$(CONFIG_PNP) += pnp/
obj-y += amba/
+
+obj-y += clk/
# Many drivers will want to use DMA so this has to be made available
# really early.
obj-$(CONFIG_DMADEVICES) += dma/
@@ -142,8 +144,6 @@ obj-$(CONFIG_VHOST) += vhost/
obj-$(CONFIG_VLYNQ) += vlynq/
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
-#common clk code
-obj-y += clk/
obj-$(CONFIG_MAILBOX) += mailbox/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 445ce28..535e782 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -77,6 +77,9 @@ config ACPI_DEBUGGER_USER
endif
+config ACPI_SPCR_TABLE
+ bool
+
config ACPI_SLEEP
bool
depends on SUSPEND || HIBERNATION
@@ -227,7 +230,6 @@ config ACPI_MCFG
config ACPI_CPPC_LIB
bool
depends on ACPI_PROCESSOR
- depends on !ACPI_CPU_FREQ_PSS
select MAILBOX
select PCC
help
@@ -462,6 +464,9 @@ source "drivers/acpi/nfit/Kconfig"
source "drivers/acpi/apei/Kconfig"
source "drivers/acpi/dptf/Kconfig"
+config ACPI_WATCHDOG
+ bool
+
config ACPI_EXTLOG
tristate "Extended Error Log support"
depends on X86_MCE && X86_LOCAL_APIC
@@ -521,4 +526,8 @@ config ACPI_CONFIGFS
userspace. The configurable ACPI groups will be visible under
/config/acpi, assuming configfs is mounted under /config.
+if ARM64
+source "drivers/acpi/arm64/Kconfig"
+endif
+
endif # ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 5ae9d85..9ed0878 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -56,6 +56,7 @@ acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o
acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o
+acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o
# These are (potentially) separate modules
@@ -81,6 +82,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
+obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o
obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o
# processor has its own "processor." module_param namespace
@@ -105,3 +107,5 @@ obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o
video-objs += acpi_video.o video_detect.o
obj-y += dptf/
+
+obj-$(CONFIG_ARM64) += arm64/
diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c
index 1daf9c4..d58fbf7 100644
--- a/drivers/acpi/acpi_apd.c
+++ b/drivers/acpi/acpi_apd.c
@@ -42,6 +42,7 @@ struct apd_private_data;
struct apd_device_desc {
unsigned int flags;
unsigned int fixed_clk_rate;
+ struct property_entry *properties;
int (*setup)(struct apd_private_data *pdata);
};
@@ -71,22 +72,35 @@ static int acpi_apd_setup(struct apd_private_data *pdata)
}
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
-static struct apd_device_desc cz_i2c_desc = {
+static const struct apd_device_desc cz_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 133000000,
};
-static struct apd_device_desc cz_uart_desc = {
+static struct property_entry uart_properties[] = {
+ PROPERTY_ENTRY_U32("reg-io-width", 4),
+ PROPERTY_ENTRY_U32("reg-shift", 2),
+ PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
+ { },
+};
+
+static const struct apd_device_desc cz_uart_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 48000000,
+ .properties = uart_properties,
};
#endif
#ifdef CONFIG_ARM64
-static struct apd_device_desc xgene_i2c_desc = {
+static const struct apd_device_desc xgene_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 100000000,
};
+
+static const struct apd_device_desc vulcan_spi_desc = {
+ .setup = acpi_apd_setup,
+ .fixed_clk_rate = 133000000,
+};
#endif
#else
@@ -125,6 +139,12 @@ static int acpi_apd_create_device(struct acpi_device *adev,
goto err_out;
}
+ if (dev_desc->properties) {
+ ret = device_add_properties(&adev->dev, dev_desc->properties);
+ if (ret)
+ goto err_out;
+ }
+
adev->driver_data = pdata;
pdev = acpi_create_platform_device(adev);
if (!IS_ERR_OR_NULL(pdev))
@@ -149,6 +169,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
#endif
#ifdef CONFIG_ARM64
{ "APMC0D0F", APD_ADDR(xgene_i2c_desc) },
+ { "BRCM900D", APD_ADDR(vulcan_spi_desc) },
#endif
{ }
};
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 357a0b8..5520102 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -75,6 +75,7 @@ struct lpss_device_desc {
const char *clk_con_id;
unsigned int prv_offset;
size_t prv_size_override;
+ struct property_entry *properties;
void (*setup)(struct lpss_private_data *pdata);
};
@@ -163,11 +164,19 @@ static const struct lpss_device_desc lpt_i2c_dev_desc = {
.prv_offset = 0x800,
};
+static struct property_entry uart_properties[] = {
+ PROPERTY_ENTRY_U32("reg-io-width", 4),
+ PROPERTY_ENTRY_U32("reg-shift", 2),
+ PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
+ { },
+};
+
static const struct lpss_device_desc lpt_uart_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
.clk_con_id = "baudclk",
.prv_offset = 0x800,
.setup = lpss_uart_setup,
+ .properties = uart_properties,
};
static const struct lpss_device_desc lpt_sdio_dev_desc = {
@@ -189,6 +198,7 @@ static const struct lpss_device_desc byt_uart_dev_desc = {
.clk_con_id = "baudclk",
.prv_offset = 0x800,
.setup = lpss_uart_setup,
+ .properties = uart_properties,
};
static const struct lpss_device_desc bsw_uart_dev_desc = {
@@ -197,6 +207,7 @@ static const struct lpss_device_desc bsw_uart_dev_desc = {
.clk_con_id = "baudclk",
.prv_offset = 0x800,
.setup = lpss_uart_setup,
+ .properties = uart_properties,
};
static const struct lpss_device_desc byt_spi_dev_desc = {
@@ -440,6 +451,12 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
goto err_out;
}
+ if (dev_desc->properties) {
+ ret = device_add_properties(&adev->dev, dev_desc->properties);
+ if (ret)
+ goto err_out;
+ }
+
adev->driver_data = pdata;
pdev = acpi_create_platform_device(adev);
if (!IS_ERR_OR_NULL(pdev)) {
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index 159f7f1..b200ae1 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
+#include <linux/pci.h>
#include <linux/platform_device.h>
#include "internal.h"
@@ -30,6 +31,22 @@ static const struct acpi_device_id forbidden_id_list[] = {
{"", 0},
};
+static void acpi_platform_fill_resource(struct acpi_device *adev,
+ const struct resource *src, struct resource *dest)
+{
+ struct device *parent;
+
+ *dest = *src;
+
+ /*
+ * If the device has parent we need to take its resources into
+ * account as well because this device might consume part of those.
+ */
+ parent = acpi_get_first_physical_node(adev->parent);
+ if (parent && dev_is_pci(parent))
+ dest->parent = pci_find_resource(to_pci_dev(parent), dest);
+}
+
/**
* acpi_create_platform_device - Create platform device for ACPI device node
* @adev: ACPI device node to create a platform device for.
@@ -70,7 +87,8 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
}
count = 0;
list_for_each_entry(rentry, &resource_list, node)
- resources[count++] = *rentry->res;
+ acpi_platform_fill_resource(adev, rentry->res,
+ &resources[count++]);
acpi_dev_free_resource_list(&resource_list);
}
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index c7ba948..3de3b6b 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -182,6 +182,11 @@ int __weak arch_register_cpu(int cpu)
void __weak arch_unregister_cpu(int cpu) {}
+int __weak acpi_map_cpu2node(acpi_handle handle, int cpu, int physid)
+{
+ return -ENODEV;
+}
+
static int acpi_processor_hotadd_init(struct acpi_processor *pr)
{
unsigned long long sta;
@@ -300,8 +305,11 @@ static int acpi_processor_get_info(struct acpi_device *device)
* Extra Processor objects may be enumerated on MP systems with
* less than the max # of CPUs. They should be ignored _iff
* they are physically not present.
+ *
+ * NOTE: Even if the processor has a cpuid, it may not be present
+ * because cpuid <-> apicid mapping is persistent now.
*/
- if (invalid_logical_cpuid(pr->id)) {
+ if (invalid_logical_cpuid(pr->id) || !cpu_present(pr->id)) {
int ret = acpi_processor_hotadd_init(pr);
if (ret)
return ret;
@@ -573,8 +581,102 @@ static struct acpi_scan_handler processor_container_handler = {
.attach = acpi_processor_container_attach,
};
+/* The number of the unique processor IDs */
+static int nr_unique_ids __initdata;
+
+/* The number of the duplicate processor IDs */
+static int nr_duplicate_ids __initdata;
+
+/* Used to store the unique processor IDs */
+static int unique_processor_ids[] __initdata = {
+ [0 ... NR_CPUS - 1] = -1,
+};
+
+/* Used to store the duplicate processor IDs */
+static int duplicate_processor_ids[] __initdata = {
+ [0 ... NR_CPUS - 1] = -1,
+};
+
+static void __init processor_validated_ids_update(int proc_id)
+{
+ int i;
+
+ if (nr_unique_ids == NR_CPUS||nr_duplicate_ids == NR_CPUS)
+ return;
+
+ /*
+ * Firstly, compare the proc_id with duplicate IDs, if the proc_id is
+ * already in the IDs, do nothing.
+ */
+ for (i = 0; i < nr_duplicate_ids; i++) {
+ if (duplicate_processor_ids[i] == proc_id)
+ return;
+ }
+
+ /*
+ * Secondly, compare the proc_id with unique IDs, if the proc_id is in
+ * the IDs, put it in the duplicate IDs.
+ */
+ for (i = 0; i < nr_unique_ids; i++) {
+ if (unique_processor_ids[i] == proc_id) {
+ duplicate_processor_ids[nr_duplicate_ids] = proc_id;
+ nr_duplicate_ids++;
+ return;
+ }
+ }
+
+ /*
+ * Lastly, the proc_id is a unique ID, put it in the unique IDs.
+ */
+ unique_processor_ids[nr_unique_ids] = proc_id;
+ nr_unique_ids++;
+}
+
+static acpi_status __init acpi_processor_ids_walk(acpi_handle handle,
+ u32 lvl,
+ void *context,
+ void **rv)
+{
+ acpi_status status;
+ union acpi_object object = { 0 };
+ struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
+
+ status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ acpi_handle_info(handle, "Not get the processor object\n");
+ else
+ processor_validated_ids_update(object.processor.proc_id);
+
+ return AE_OK;
+}
+
+static void __init acpi_processor_check_duplicates(void)
+{
+ /* Search all processor nodes in ACPI namespace */
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ acpi_processor_ids_walk,
+ NULL, NULL, NULL);
+}
+
+bool __init acpi_processor_validate_proc_id(int proc_id)
+{
+ int i;
+
+ /*
+ * compare the proc_id with duplicate IDs, if the proc_id is already
+ * in the duplicate IDs, return true, otherwise, return false.
+ */
+ for (i = 0; i < nr_duplicate_ids; i++) {
+ if (duplicate_processor_ids[i] == proc_id)
+ return true;
+ }
+ return false;
+}
+
void __init acpi_processor_init(void)
{
+ acpi_processor_check_duplicates();
acpi_scan_add_handler_with_hotplug(&processor_handler, "processor");
acpi_scan_add_handler(&processor_container_handler);
}
diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c
new file mode 100644
index 0000000..13caebd
--- /dev/null
+++ b/drivers/acpi/acpi_watchdog.c
@@ -0,0 +1,123 @@
+/*
+ * ACPI watchdog table parsing support.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) "ACPI: watchdog: " fmt
+
+#include <linux/acpi.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+
+#include "internal.h"
+
+/**
+ * Returns true if this system should prefer ACPI based watchdog instead of
+ * the native one (which are typically the same hardware).
+ */
+bool acpi_has_watchdog(void)
+{
+ struct acpi_table_header hdr;
+
+ if (acpi_disabled)
+ return false;
+
+ return ACPI_SUCCESS(acpi_get_table_header(ACPI_SIG_WDAT, 0, &hdr));
+}
+EXPORT_SYMBOL_GPL(acpi_has_watchdog);
+
+void __init acpi_watchdog_init(void)
+{
+ const struct acpi_wdat_entry *entries;
+ const struct acpi_table_wdat *wdat;
+ struct list_head resource_list;
+ struct resource_entry *rentry;
+ struct platform_device *pdev;
+ struct resource *resources;
+ size_t nresources = 0;
+ acpi_status status;
+ int i;
+
+ status = acpi_get_table(ACPI_SIG_WDAT, 0,
+ (struct acpi_table_header **)&wdat);
+ if (ACPI_FAILURE(status)) {
+ /* It is fine if there is no WDAT */
+ return;
+ }
+
+ /* Watchdog disabled by BIOS */
+ if (!(wdat->flags & ACPI_WDAT_ENABLED))
+ return;
+
+ /* Skip legacy PCI WDT devices */
+ if (wdat->pci_segment != 0xff || wdat->pci_bus != 0xff ||
+ wdat->pci_device != 0xff || wdat->pci_function != 0xff)
+ return;
+
+ INIT_LIST_HEAD(&resource_list);
+
+ entries = (struct acpi_wdat_entry *)(wdat + 1);
+ for (i = 0; i < wdat->entries; i++) {
+ const struct acpi_generic_address *gas;
+ struct resource_entry *rentry;
+ struct resource res;
+ bool found;
+
+ gas = &entries[i].register_region;
+
+ res.start = gas->address;
+ if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ res.flags = IORESOURCE_MEM;
+ res.end = res.start + ALIGN(gas->access_width, 4);
+ } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ res.flags = IORESOURCE_IO;
+ res.end = res.start + gas->access_width;
+ } else {
+ pr_warn("Unsupported address space: %u\n",
+ gas->space_id);
+ goto fail_free_resource_list;
+ }
+
+ found = false;
+ resource_list_for_each_entry(rentry, &resource_list) {
+ if (resource_contains(rentry->res, &res)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ rentry = resource_list_create_entry(NULL, 0);
+ if (!rentry)
+ goto fail_free_resource_list;
+
+ *rentry->res = res;
+ resource_list_add_tail(rentry, &resource_list);
+ nresources++;
+ }
+ }
+
+ resources = kcalloc(nresources, sizeof(*resources), GFP_KERNEL);
+ if (!resources)
+ goto fail_free_resource_list;
+
+ i = 0;
+ resource_list_for_each_entry(rentry, &resource_list)
+ resources[i++] = *rentry->res;
+
+ pdev = platform_device_register_simple("wdat_wdt", PLATFORM_DEVID_NONE,
+ resources, nresources);
+ if (IS_ERR(pdev))
+ pr_err("Failed to create platform device\n");
+
+ kfree(resources);
+
+fail_free_resource_list:
+ resource_list_free(&resource_list);
+}
diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile
index 227bb7b..32d93ed 100644
--- a/drivers/acpi/acpica/Makefile
+++ b/drivers/acpi/acpica/Makefile
@@ -175,6 +175,7 @@ acpi-y += \
utresrc.o \
utstate.o \
utstring.o \
+ utstrtoul64.o \
utxface.o \
utxfinit.o \
utxferror.o \
diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h
index ca2c060..0bd6307 100644
--- a/drivers/acpi/acpica/acapps.h
+++ b/drivers/acpi/acpica/acapps.h
@@ -44,7 +44,9 @@
#ifndef _ACAPPS
#define _ACAPPS
-#include <stdio.h>
+#ifdef ACPI_USE_STANDARD_HEADERS
+#include <sys/stat.h>
+#endif /* ACPI_USE_STANDARD_HEADERS */
/* Common info for tool signons */
@@ -81,13 +83,13 @@
/* Macros for usage messages */
#define ACPI_USAGE_HEADER(usage) \
- acpi_os_printf ("Usage: %s\nOptions:\n", usage);
+ printf ("Usage: %s\nOptions:\n", usage);
#define ACPI_USAGE_TEXT(description) \
- acpi_os_printf (description);
+ printf (description);
#define ACPI_OPTION(name, description) \
- acpi_os_printf (" %-20s%s\n", name, description);
+ printf (" %-20s%s\n", name, description);
/* Check for unexpected exceptions */
diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h
index f6404ea..94737f8 100644
--- a/drivers/acpi/acpica/acdebug.h
+++ b/drivers/acpi/acpica/acdebug.h
@@ -155,7 +155,7 @@ acpi_status acpi_db_disassemble_method(char *name);
void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op);
-void acpi_db_batch_execute(char *count_arg);
+void acpi_db_evaluate_predefined_names(void);
/*
* dbnames - namespace commands
diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 77af91c..92fa47c 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -86,6 +86,9 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info);
acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);
acpi_status
+acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked);
+
+acpi_status
acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info);
acpi_status
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index fded776..750fa82 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -317,6 +317,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_ignore_noop_operator, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_cstyle_disassembly, TRUE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_force_aml_disassembly, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_opt_verbose, TRUE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_emit_external_opcodes, FALSE);
ACPI_GLOBAL(u8, acpi_gbl_dm_opt_disasm);
ACPI_GLOBAL(u8, acpi_gbl_dm_opt_listing);
@@ -382,6 +383,7 @@ ACPI_GLOBAL(const char, *acpi_gbl_pld_shape_list[]);
ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL);
ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_output_file, NULL);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_debug_timeout, FALSE);
/* Print buffer */
diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h
index 13331d7..dff1207 100644
--- a/drivers/acpi/acpica/aclocal.h
+++ b/drivers/acpi/acpica/aclocal.h
@@ -484,6 +484,7 @@ struct acpi_gpe_event_info {
u8 flags; /* Misc info about this GPE */
u8 gpe_number; /* This GPE */
u8 runtime_count; /* References to a run GPE */
+ u8 disable_for_dispatch; /* Masked during dispatching */
};
/* Information about a GPE register pair, one per each status/enable pair in an array */
@@ -494,6 +495,7 @@ struct acpi_gpe_register_info {
u16 base_gpe_number; /* Base GPE number for this register */
u8 enable_for_wake; /* GPEs to keep enabled when sleeping */
u8 enable_for_run; /* GPEs to keep enabled when running */
+ u8 mask_for_run; /* GPEs to keep masked when running */
u8 enable_mask; /* Current mask of enabled GPEs */
};
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index f33a4ba..bb7fca1 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -130,6 +130,9 @@ acpi_status
acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node);
acpi_status
+acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node);
+
+acpi_status
acpi_ns_one_complete_parse(u32 pass_number,
u32 table_index,
struct acpi_namespace_node *start_node);
@@ -296,6 +299,11 @@ u8
acpi_ns_pattern_match(struct acpi_namespace_node *obj_node, char *search_for);
acpi_status
+acpi_ns_get_node_unlocked(struct acpi_namespace_node *prefix_node,
+ const char *external_pathname,
+ u32 flags, struct acpi_namespace_node **out_node);
+
+acpi_status
acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
const char *external_pathname,
u32 flags, struct acpi_namespace_node **out_node);
diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h
index fc30577..939d411 100644
--- a/drivers/acpi/acpica/acparser.h
+++ b/drivers/acpi/acpica/acparser.h
@@ -78,6 +78,8 @@ extern const u8 acpi_gbl_long_op_index[];
*/
acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info);
+acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info);
+
/*
* psargs - Parse AML opcode arguments
*/
diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h
index cd5a135..e85953b 100644
--- a/drivers/acpi/acpica/actables.h
+++ b/drivers/acpi/acpica/actables.h
@@ -123,6 +123,14 @@ acpi_tb_install_standard_table(acpi_physical_address address,
void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc);
+acpi_status
+acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node);
+
+acpi_status
+acpi_tb_install_and_load_table(struct acpi_table_header *table,
+ acpi_physical_address address,
+ u8 flags, u8 override, u32 *table_index);
+
void acpi_tb_terminate(void);
acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index);
@@ -155,10 +163,6 @@ void
acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
u8 override, u32 *table_index);
-acpi_status
-acpi_tb_install_fixed_table(acpi_physical_address address,
- char *signature, u32 *table_index);
-
acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address);
/*
diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h
index a7dbb2b..0a1b53c 100644
--- a/drivers/acpi/acpica/acutils.h
+++ b/drivers/acpi/acpica/acutils.h
@@ -114,13 +114,25 @@ extern const char *acpi_gbl_pt_decode[];
/*
* Common error message prefixes
*/
+#ifndef ACPI_MSG_ERROR
#define ACPI_MSG_ERROR "ACPI Error: "
+#endif
+#ifndef ACPI_MSG_EXCEPTION
#define ACPI_MSG_EXCEPTION "ACPI Exception: "
+#endif
+#ifndef ACPI_MSG_WARNING
#define ACPI_MSG_WARNING "ACPI Warning: "
+#endif
+#ifndef ACPI_MSG_INFO
#define ACPI_MSG_INFO "ACPI: "
+#endif
+#ifndef ACPI_MSG_BIOS_ERROR
#define ACPI_MSG_BIOS_ERROR "ACPI BIOS Error (bug): "
+#endif
+#ifndef ACPI_MSG_BIOS_WARNING
#define ACPI_MSG_BIOS_WARNING "ACPI BIOS Warning (bug): "
+#endif
/*
* Common message suffix
@@ -184,14 +196,15 @@ void acpi_ut_strlwr(char *src_string);
int acpi_ut_stricmp(char *string1, char *string2);
-acpi_status
-acpi_ut_strtoul64(char *string,
- u32 base, u32 max_integer_byte_width, u64 *ret_integer);
-
-/* Values for max_integer_byte_width above */
+acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *ret_integer);
-#define ACPI_MAX32_BYTE_WIDTH 4
-#define ACPI_MAX64_BYTE_WIDTH 8
+/*
+ * Values for Flags above
+ * Note: LIMIT values correspond to acpi_gbl_integer_byte_width values (4/8)
+ */
+#define ACPI_STRTOUL_32BIT 0x04 /* 4 bytes */
+#define ACPI_STRTOUL_64BIT 0x08 /* 8 bytes */
+#define ACPI_STRTOUL_BASE16 0x10 /* Default: Base10/16 */
/*
* utglobal - Global data structures and procedures
@@ -221,6 +234,8 @@ const char *acpi_ut_get_event_name(u32 event_id);
char acpi_ut_hex_to_ascii_char(u64 integer, u32 position);
+acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte);
+
u8 acpi_ut_ascii_char_to_hex(int hex_char);
u8 acpi_ut_valid_object_type(acpi_object_type type);
@@ -318,6 +333,11 @@ acpi_ut_ptr_exit(u32 line_number,
const char *module_name, u32 component_id, u8 *ptr);
void
+acpi_ut_str_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, const char *string);
+
+void
acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id);
void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 offset);
@@ -707,25 +727,6 @@ const struct ah_device_id *acpi_ah_match_hardware_id(char *hid);
const char *acpi_ah_match_uuid(u8 *data);
/*
- * utprint - printf/vprintf output functions
- */
-const char *acpi_ut_scan_number(const char *string, u64 *number_ptr);
-
-const char *acpi_ut_print_number(char *string, u64 number);
-
-int
-acpi_ut_vsnprintf(char *string,
- acpi_size size, const char *format, va_list args);
-
-int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...);
-
-#ifdef ACPI_APPLICATION
-int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args);
-
-int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...);
-#endif
-
-/*
* utuuid -- UUID support functions
*/
#if (defined ACPI_ASL_COMPILER || defined ACPI_EXEC_APP || defined ACPI_HELP_APP)
diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c
index 7cd07b2..147ce88 100644
--- a/drivers/acpi/acpica/dbconvert.c
+++ b/drivers/acpi/acpica/dbconvert.c
@@ -277,9 +277,10 @@ acpi_db_convert_to_object(acpi_object_type type,
default:
object->type = ACPI_TYPE_INTEGER;
- status =
- acpi_ut_strtoul64(string, 16, acpi_gbl_integer_byte_width,
- &object->integer.value);
+ status = acpi_ut_strtoul64(string,
+ (acpi_gbl_integer_byte_width |
+ ACPI_STRTOUL_BASE16),
+ &object->integer.value);
break;
}
diff --git a/drivers/acpi/acpica/dbexec.c b/drivers/acpi/acpica/dbexec.c
index 12df291..fe3da7c 100644
--- a/drivers/acpi/acpica/dbexec.c
+++ b/drivers/acpi/acpica/dbexec.c
@@ -392,42 +392,48 @@ acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags)
acpi_db_execution_walk, NULL, NULL,
NULL);
return;
- } else {
- name_string = ACPI_ALLOCATE(strlen(name) + 1);
- if (!name_string) {
- return;
- }
+ }
- memset(&acpi_gbl_db_method_info, 0,
- sizeof(struct acpi_db_method_info));
+ name_string = ACPI_ALLOCATE(strlen(name) + 1);
+ if (!name_string) {
+ return;
+ }
- strcpy(name_string, name);
- acpi_ut_strupr(name_string);
- acpi_gbl_db_method_info.name = name_string;
- acpi_gbl_db_method_info.args = args;
- acpi_gbl_db_method_info.types = types;
- acpi_gbl_db_method_info.flags = flags;
+ memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
+ strcpy(name_string, name);
+ acpi_ut_strupr(name_string);
- return_obj.pointer = NULL;
- return_obj.length = ACPI_ALLOCATE_BUFFER;
+ /* Subcommand to Execute all predefined names in the namespace */
- status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
- if (ACPI_FAILURE(status)) {
- ACPI_FREE(name_string);
- return;
- }
+ if (!strncmp(name_string, "PREDEF", 6)) {
+ acpi_db_evaluate_predefined_names();
+ ACPI_FREE(name_string);
+ return;
+ }
- /* Get the NS node, determines existence also */
+ acpi_gbl_db_method_info.name = name_string;
+ acpi_gbl_db_method_info.args = args;
+ acpi_gbl_db_method_info.types = types;
+ acpi_gbl_db_method_info.flags = flags;
- status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
- &acpi_gbl_db_method_info.method);
- if (ACPI_SUCCESS(status)) {
- status =
- acpi_db_execute_method(&acpi_gbl_db_method_info,
- &return_obj);
- }
+ return_obj.pointer = NULL;
+ return_obj.length = ACPI_ALLOCATE_BUFFER;
+
+ status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
+ if (ACPI_FAILURE(status)) {
ACPI_FREE(name_string);
+ return;
+ }
+
+ /* Get the NS node, determines existence also */
+
+ status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
+ &acpi_gbl_db_method_info.method);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_db_execute_method(&acpi_gbl_db_method_info,
+ &return_obj);
}
+ ACPI_FREE(name_string);
/*
* Allow any handlers in separate threads to complete.
diff --git a/drivers/acpi/acpica/dbfileio.c b/drivers/acpi/acpica/dbfileio.c
index 4832879..6f05b8c 100644
--- a/drivers/acpi/acpica/dbfileio.c
+++ b/drivers/acpi/acpica/dbfileio.c
@@ -46,14 +46,12 @@
#include "accommon.h"
#include "acdebug.h"
#include "actables.h"
-#include <stdio.h>
-#ifdef ACPI_APPLICATION
-#include "acapps.h"
-#endif
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME("dbfileio")
+#ifdef ACPI_APPLICATION
+#include "acapps.h"
#ifdef ACPI_DEBUGGER
/*******************************************************************************
*
@@ -69,8 +67,6 @@ ACPI_MODULE_NAME("dbfileio")
void acpi_db_close_debug_file(void)
{
-#ifdef ACPI_APPLICATION
-
if (acpi_gbl_debug_file) {
fclose(acpi_gbl_debug_file);
acpi_gbl_debug_file = NULL;
@@ -78,7 +74,6 @@ void acpi_db_close_debug_file(void)
acpi_os_printf("Debug output file %s closed\n",
acpi_gbl_db_debug_filename);
}
-#endif
}
/*******************************************************************************
@@ -96,8 +91,6 @@ void acpi_db_close_debug_file(void)
void acpi_db_open_debug_file(char *name)
{
-#ifdef ACPI_APPLICATION
-
acpi_db_close_debug_file();
acpi_gbl_debug_file = fopen(name, "w+");
if (!acpi_gbl_debug_file) {
@@ -109,8 +102,6 @@ void acpi_db_open_debug_file(char *name)
strncpy(acpi_gbl_db_debug_filename, name,
sizeof(acpi_gbl_db_debug_filename));
acpi_gbl_db_output_to_file = TRUE;
-
-#endif
}
#endif
@@ -152,12 +143,13 @@ acpi_status acpi_db_load_tables(struct acpi_new_table_desc *list_head)
return (status);
}
- fprintf(stderr,
- "Acpi table [%4.4s] successfully installed and loaded\n",
- table->signature);
+ acpi_os_printf
+ ("Acpi table [%4.4s] successfully installed and loaded\n",
+ table->signature);
table_list_head = table_list_head->next;
}
return (AE_OK);
}
+#endif
diff --git a/drivers/acpi/acpica/dbinput.c b/drivers/acpi/acpica/dbinput.c
index 7cd5d2e..068214f 100644
--- a/drivers/acpi/acpica/dbinput.c
+++ b/drivers/acpi/acpica/dbinput.c
@@ -286,6 +286,8 @@ static const struct acpi_db_command_help acpi_gbl_db_command_help[] = {
{1, " \"Ascii String\"", "String method argument\n"},
{1, " (Hex Byte List)", "Buffer method argument\n"},
{1, " [Package Element List]", "Package method argument\n"},
+ {5, " Execute predefined",
+ "Execute all predefined (public) methods\n"},
{1, " Go", "Allow method to run to completion\n"},
{1, " Information", "Display info about the current method\n"},
{1, " Into", "Step into (not over) a method call\n"},
diff --git a/drivers/acpi/acpica/dbmethod.c b/drivers/acpi/acpica/dbmethod.c
index f17a86f..314b94c 100644
--- a/drivers/acpi/acpica/dbmethod.c
+++ b/drivers/acpi/acpica/dbmethod.c
@@ -52,6 +52,11 @@
#define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME("dbmethod")
+/* Local prototypes */
+static acpi_status
+acpi_db_walk_for_execute(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value);
+
/*******************************************************************************
*
* FUNCTION: acpi_db_set_method_breakpoint
@@ -66,6 +71,7 @@ ACPI_MODULE_NAME("dbmethod")
* AML offset
*
******************************************************************************/
+
void
acpi_db_set_method_breakpoint(char *location,
struct acpi_walk_state *walk_state,
@@ -367,3 +373,129 @@ acpi_status acpi_db_disassemble_method(char *name)
acpi_ut_release_owner_id(&obj_desc->method.owner_id);
return (AE_OK);
}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_walk_for_execute
+ *
+ * PARAMETERS: Callback from walk_namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Batch execution module. Currently only executes predefined
+ * ACPI names.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_db_walk_for_execute(acpi_handle obj_handle,
+ u32 nesting_level, void *context, void **return_value)
+{
+ struct acpi_namespace_node *node =
+ (struct acpi_namespace_node *)obj_handle;
+ struct acpi_db_execute_walk *info =
+ (struct acpi_db_execute_walk *)context;
+ struct acpi_buffer return_obj;
+ acpi_status status;
+ char *pathname;
+ u32 i;
+ struct acpi_device_info *obj_info;
+ struct acpi_object_list param_objects;
+ union acpi_object params[ACPI_METHOD_NUM_ARGS];
+ const union acpi_predefined_info *predefined;
+
+ predefined = acpi_ut_match_predefined_method(node->name.ascii);
+ if (!predefined) {
+ return (AE_OK);
+ }
+
+ if (node->type == ACPI_TYPE_LOCAL_SCOPE) {
+ return (AE_OK);
+ }
+
+ pathname = acpi_ns_get_external_pathname(node);
+ if (!pathname) {
+ return (AE_OK);
+ }
+
+ /* Get the object info for number of method parameters */
+
+ status = acpi_get_object_info(obj_handle, &obj_info);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+
+ param_objects.pointer = NULL;
+ param_objects.count = 0;
+
+ if (obj_info->type == ACPI_TYPE_METHOD) {
+
+ /* Setup default parameters */
+
+ for (i = 0; i < obj_info->param_count; i++) {
+ params[i].type = ACPI_TYPE_INTEGER;
+ params[i].integer.value = 1;
+ }
+
+ param_objects.pointer = params;
+ param_objects.count = obj_info->param_count;
+ }
+
+ ACPI_FREE(obj_info);
+ return_obj.pointer = NULL;
+ return_obj.length = ACPI_ALLOCATE_BUFFER;
+
+ /* Do the actual method execution */
+
+ acpi_gbl_method_executing = TRUE;
+
+ status = acpi_evaluate_object(node, NULL, &param_objects, &return_obj);
+
+ acpi_os_printf("%-32s returned %s\n", pathname,
+ acpi_format_exception(status));
+ acpi_gbl_method_executing = FALSE;
+ ACPI_FREE(pathname);
+
+ /* Ignore status from method execution */
+
+ status = AE_OK;
+
+ /* Update count, check if we have executed enough methods */
+
+ info->count++;
+ if (info->count >= info->max_count) {
+ status = AE_CTRL_TERMINATE;
+ }
+
+ return (status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_db_evaluate_predefined_names
+ *
+ * PARAMETERS: None
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Namespace batch execution. Execute predefined names in the
+ * namespace, up to the max count, if specified.
+ *
+ ******************************************************************************/
+
+void acpi_db_evaluate_predefined_names(void)
+{
+ struct acpi_db_execute_walk info;
+
+ info.count = 0;
+ info.max_count = ACPI_UINT32_MAX;
+
+ /* Search all nodes in namespace */
+
+ (void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_db_walk_for_execute,
+ NULL, (void *)&info, NULL);
+
+ acpi_os_printf("Evaluated %u predefined names in the namespace\n",
+ info.count);
+}
diff --git a/drivers/acpi/acpica/dbobject.c b/drivers/acpi/acpica/dbobject.c
index 1d59e8b..08eaaf3 100644
--- a/drivers/acpi/acpica/dbobject.c
+++ b/drivers/acpi/acpica/dbobject.c
@@ -142,11 +142,11 @@ void acpi_db_decode_internal_object(union acpi_operand_object *obj_desc)
case ACPI_TYPE_STRING:
- acpi_os_printf("(%u) \"%.24s",
+ acpi_os_printf("(%u) \"%.60s",
obj_desc->string.length,
obj_desc->string.pointer);
- if (obj_desc->string.length > 24) {
+ if (obj_desc->string.length > 60) {
acpi_os_printf("...");
} else {
acpi_os_printf("\"");
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index 47c7b52..32e9ddc 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -99,11 +99,14 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
"Method auto-serialization parse [%4.4s] %p\n",
acpi_ut_get_node_name(node), node));
+ acpi_ex_enter_interpreter();
+
/* Create/Init a root op for the method parse tree */
op = acpi_ps_alloc_op(AML_METHOD_OP, obj_desc->method.aml_start);
if (!op) {
- return_ACPI_STATUS(AE_NO_MEMORY);
+ status = AE_NO_MEMORY;
+ goto unlock;
}
acpi_ps_set_name(op, node->name.integer);
@@ -115,7 +118,8 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL);
if (!walk_state) {
acpi_ps_free_op(op);
- return_ACPI_STATUS(AE_NO_MEMORY);
+ status = AE_NO_MEMORY;
+ goto unlock;
}
status = acpi_ds_init_aml_walk(walk_state, op, node,
@@ -134,6 +138,8 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
status = acpi_ps_parse_aml(walk_state);
acpi_ps_delete_parse_tree(op);
+unlock:
+ acpi_ex_exit_interpreter();
return_ACPI_STATUS(status);
}
@@ -757,8 +763,10 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
/* Delete any direct children of (created by) this method */
+ (void)acpi_ex_exit_interpreter();
acpi_ns_delete_namespace_subtree(walk_state->
method_node);
+ (void)acpi_ex_enter_interpreter();
/*
* Delete any objects that were created by this method
@@ -769,9 +777,11 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
*/
if (method_desc->method.
info_flags & ACPI_METHOD_MODIFIED_NAMESPACE) {
+ (void)acpi_ex_exit_interpreter();
acpi_ns_delete_namespace_by_owner(method_desc->
method.
owner_id);
+ (void)acpi_ex_enter_interpreter();
method_desc->method.info_flags &=
~ACPI_METHOD_MODIFIED_NAMESPACE;
}
diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c
index f393de9..7d8ef52 100644
--- a/drivers/acpi/acpica/dsutils.c
+++ b/drivers/acpi/acpica/dsutils.c
@@ -565,15 +565,14 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
status = AE_OK;
} else if (parent_op->common.aml_opcode ==
AML_EXTERNAL_OP) {
-
- /* TBD: May only be temporary */
-
- obj_desc =
- acpi_ut_create_string_object((acpi_size)name_length);
-
- strncpy(obj_desc->string.pointer,
- name_string, name_length);
- status = AE_OK;
+ /*
+ * This opcode should never appear here. It is used only
+ * by AML disassemblers and is surrounded by an If(0)
+ * by the ASL compiler.
+ *
+ * Therefore, if we see it here, it is a serious error.
+ */
+ status = AE_AML_BAD_OPCODE;
} else {
/*
* We just plain didn't find it -- which is a
diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c
index 402ecc5..438597c 100644
--- a/drivers/acpi/acpica/dswexec.c
+++ b/drivers/acpi/acpica/dswexec.c
@@ -133,7 +133,8 @@ acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state,
* Result of predicate evaluation must be an Integer
* object. Implicitly convert the argument if necessary.
*/
- status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc, 16);
+ status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc,
+ ACPI_STRTOUL_BASE16);
if (ACPI_FAILURE(status)) {
goto cleanup;
}
diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c
index 762db3f..028b22a 100644
--- a/drivers/acpi/acpica/dswload2.c
+++ b/drivers/acpi/acpica/dswload2.c
@@ -605,16 +605,13 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
-
- acpi_ex_exit_interpreter();
}
+ acpi_ex_exit_interpreter();
status =
acpi_ev_initialize_region
(acpi_ns_get_attached_object(node), FALSE);
- if (walk_state->method_node) {
- acpi_ex_enter_interpreter();
- }
+ acpi_ex_enter_interpreter();
if (ACPI_FAILURE(status)) {
/*
diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c
index 4b4949c..bdb10be 100644
--- a/drivers/acpi/acpica/evgpe.c
+++ b/drivers/acpi/acpica/evgpe.c
@@ -130,6 +130,60 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
/*******************************************************************************
*
+ * FUNCTION: acpi_ev_mask_gpe
+ *
+ * PARAMETERS: gpe_event_info - GPE to be blocked/unblocked
+ * is_masked - Whether the GPE is masked or not
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Unconditionally mask/unmask a GPE during runtime.
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked)
+{
+ struct acpi_gpe_register_info *gpe_register_info;
+ u32 register_bit;
+
+ ACPI_FUNCTION_TRACE(ev_mask_gpe);
+
+ gpe_register_info = gpe_event_info->register_info;
+ if (!gpe_register_info) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
+
+ /* Perform the action */
+
+ if (is_masked) {
+ if (register_bit & gpe_register_info->mask_for_run) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
+ ACPI_SET_BIT(gpe_register_info->mask_for_run, (u8)register_bit);
+ } else {
+ if (!(register_bit & gpe_register_info->mask_for_run)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ ACPI_CLEAR_BIT(gpe_register_info->mask_for_run,
+ (u8)register_bit);
+ if (gpe_event_info->runtime_count
+ && !gpe_event_info->disable_for_dispatch) {
+ (void)acpi_hw_low_set_gpe(gpe_event_info,
+ ACPI_GPE_ENABLE);
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ev_add_gpe_reference
*
* PARAMETERS: gpe_event_info - Add a reference to this GPE
@@ -674,6 +728,7 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info)
* in the event_info.
*/
(void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE);
+ gpe_event_info->disable_for_dispatch = FALSE;
return (AE_OK);
}
@@ -737,6 +792,8 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
}
}
+ gpe_event_info->disable_for_dispatch = TRUE;
+
/*
* Dispatch the GPE to either an installed handler or the control
* method associated with this GPE (_Lxx or _Exx). If a handler
diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c
index 7dc7547..16ce483 100644
--- a/drivers/acpi/acpica/evgpeinit.c
+++ b/drivers/acpi/acpica/evgpeinit.c
@@ -323,7 +323,9 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
struct acpi_gpe_walk_info *walk_info =
ACPI_CAST_PTR(struct acpi_gpe_walk_info, context);
struct acpi_gpe_event_info *gpe_event_info;
+ acpi_status status;
u32 gpe_number;
+ u8 temp_gpe_number;
char name[ACPI_NAME_SIZE + 1];
u8 type;
@@ -377,8 +379,8 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
/* 4) The last two characters of the name are the hex GPE Number */
- gpe_number = strtoul(&name[2], NULL, 16);
- if (gpe_number == ACPI_UINT32_MAX) {
+ status = acpi_ut_ascii_to_hex_byte(&name[2], &temp_gpe_number);
+ if (ACPI_FAILURE(status)) {
/* Conversion failed; invalid method, just ignore it */
@@ -390,6 +392,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
/* Ensure that we have a valid GPE number for this GPE block */
+ gpe_number = (u32)temp_gpe_number;
gpe_event_info =
acpi_ev_low_get_gpe_info(gpe_number, walk_info->gpe_block);
if (!gpe_event_info) {
diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c
index b6ea9c0..3843f1f 100644
--- a/drivers/acpi/acpica/evrgnini.c
+++ b/drivers/acpi/acpica/evrgnini.c
@@ -553,7 +553,8 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
*
* See acpi_ns_exec_module_code
*/
- if (obj_desc->method.
+ if (!acpi_gbl_parse_table_as_term_list &&
+ obj_desc->method.
info_flags & ACPI_METHOD_MODULE_LEVEL) {
handler_obj =
obj_desc->method.dispatch.handler;
diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c
index 17cfef7..d7a3b27 100644
--- a/drivers/acpi/acpica/evxfgpe.c
+++ b/drivers/acpi/acpica/evxfgpe.c
@@ -235,11 +235,13 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)
case ACPI_GPE_ENABLE:
status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
+ gpe_event_info->disable_for_dispatch = FALSE;
break;
case ACPI_GPE_DISABLE:
status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
+ gpe_event_info->disable_for_dispatch = TRUE;
break;
default:
@@ -257,6 +259,47 @@ ACPI_EXPORT_SYMBOL(acpi_set_gpe)
/*******************************************************************************
*
+ * FUNCTION: acpi_mask_gpe
+ *
+ * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
+ * gpe_number - GPE level within the GPE block
+ * is_masked - Whether the GPE is masked or not
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Unconditionally mask/unmask the an individual GPE, ex., to
+ * prevent a GPE flooding.
+ *
+ ******************************************************************************/
+acpi_status acpi_mask_gpe(acpi_handle gpe_device, u32 gpe_number, u8 is_masked)
+{
+ struct acpi_gpe_event_info *gpe_event_info;
+ acpi_status status;
+ acpi_cpu_flags flags;
+
+ ACPI_FUNCTION_TRACE(acpi_mask_gpe);
+
+ flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
+
+ /* Ensure that we have a valid GPE number */
+
+ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
+ if (!gpe_event_info) {
+ status = AE_BAD_PARAMETER;
+ goto unlock_and_exit;
+ }
+
+ status = acpi_ev_mask_gpe(gpe_event_info, is_masked);
+
+unlock_and_exit:
+ acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
+ return_ACPI_STATUS(status);
+}
+
+ACPI_EXPORT_SYMBOL(acpi_mask_gpe)
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_mark_gpe_for_wake
*
* PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
diff --git a/drivers/acpi/acpica/exconcat.c b/drivers/acpi/acpica/exconcat.c
index 2423fe0..5429c2a 100644
--- a/drivers/acpi/acpica/exconcat.c
+++ b/drivers/acpi/acpica/exconcat.c
@@ -156,7 +156,7 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0,
status =
acpi_ex_convert_to_integer(local_operand1, &temp_operand1,
- 16);
+ ACPI_STRTOUL_BASE16);
break;
case ACPI_TYPE_BUFFER:
diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c
index a1d177d..718428b 100644
--- a/drivers/acpi/acpica/exconfig.c
+++ b/drivers/acpi/acpica/exconfig.c
@@ -55,9 +55,7 @@ ACPI_MODULE_NAME("exconfig")
/* Local prototypes */
static acpi_status
-acpi_ex_add_table(u32 table_index,
- struct acpi_namespace_node *parent_node,
- union acpi_operand_object **ddb_handle);
+acpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle);
static acpi_status
acpi_ex_region_read(union acpi_operand_object *obj_desc,
@@ -79,13 +77,9 @@ acpi_ex_region_read(union acpi_operand_object *obj_desc,
******************************************************************************/
static acpi_status
-acpi_ex_add_table(u32 table_index,
- struct acpi_namespace_node *parent_node,
- union acpi_operand_object **ddb_handle)
+acpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle)
{
union acpi_operand_object *obj_desc;
- acpi_status status;
- acpi_owner_id owner_id;
ACPI_FUNCTION_TRACE(ex_add_table);
@@ -100,39 +94,8 @@ acpi_ex_add_table(u32 table_index,
obj_desc->common.flags |= AOPOBJ_DATA_VALID;
obj_desc->reference.class = ACPI_REFCLASS_TABLE;
- *ddb_handle = obj_desc;
-
- /* Install the new table into the local data structures */
-
obj_desc->reference.value = table_index;
-
- /* Add the table to the namespace */
-
- status = acpi_ns_load_table(table_index, parent_node);
- if (ACPI_FAILURE(status)) {
- acpi_ut_remove_reference(obj_desc);
- *ddb_handle = NULL;
- return_ACPI_STATUS(status);
- }
-
- /* Execute any module-level code that was found in the table */
-
- acpi_ex_exit_interpreter();
- if (acpi_gbl_group_module_level_code) {
- acpi_ns_exec_module_code_list();
- }
- acpi_ex_enter_interpreter();
-
- /*
- * Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is
- * responsible for discovering any new wake GPEs by running _PRW methods
- * that may have been loaded by this table.
- */
- status = acpi_tb_get_owner_id(table_index, &owner_id);
- if (ACPI_SUCCESS(status)) {
- acpi_ev_update_gpes(owner_id);
- }
-
+ *ddb_handle = obj_desc;
return_ACPI_STATUS(AE_OK);
}
@@ -159,16 +122,17 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
struct acpi_namespace_node *start_node;
struct acpi_namespace_node *parameter_node = NULL;
union acpi_operand_object *ddb_handle;
- struct acpi_table_header *table;
u32 table_index;
ACPI_FUNCTION_TRACE(ex_load_table_op);
/* Find the ACPI table in the RSDT/XSDT */
+ acpi_ex_exit_interpreter();
status = acpi_tb_find_table(operand[0]->string.pointer,
operand[1]->string.pointer,
operand[2]->string.pointer, &table_index);
+ acpi_ex_enter_interpreter();
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) {
return_ACPI_STATUS(status);
@@ -197,9 +161,10 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
* Find the node referenced by the root_path_string. This is the
* location within the namespace where the table will be loaded.
*/
- status =
- acpi_ns_get_node(start_node, operand[3]->string.pointer,
- ACPI_NS_SEARCH_PARENT, &parent_node);
+ status = acpi_ns_get_node_unlocked(start_node,
+ operand[3]->string.pointer,
+ ACPI_NS_SEARCH_PARENT,
+ &parent_node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -219,9 +184,10 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
/* Find the node referenced by the parameter_path_string */
- status =
- acpi_ns_get_node(start_node, operand[4]->string.pointer,
- ACPI_NS_SEARCH_PARENT, &parameter_node);
+ status = acpi_ns_get_node_unlocked(start_node,
+ operand[4]->string.pointer,
+ ACPI_NS_SEARCH_PARENT,
+ &parameter_node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -229,7 +195,15 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
/* Load the table into the namespace */
- status = acpi_ex_add_table(table_index, parent_node, &ddb_handle);
+ ACPI_INFO(("Dynamic OEM Table Load:"));
+ acpi_ex_exit_interpreter();
+ status = acpi_tb_load_table(table_index, parent_node);
+ acpi_ex_enter_interpreter();
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ex_add_table(table_index, &ddb_handle);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -252,19 +226,6 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
}
}
- status = acpi_get_table_by_index(table_index, &table);
- if (ACPI_SUCCESS(status)) {
- ACPI_INFO(("Dynamic OEM Table Load:"));
- acpi_tb_print_table_header(0, table);
- }
-
- /* Invoke table handler if present */
-
- if (acpi_gbl_table_handler) {
- (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
- acpi_gbl_table_handler_context);
- }
-
*return_desc = ddb_handle;
return_ACPI_STATUS(status);
}
@@ -475,13 +436,12 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
/* Install the new table into the local data structures */
ACPI_INFO(("Dynamic OEM Table Load:"));
- (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
-
- status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table),
- ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL,
- TRUE, TRUE, &table_index);
-
- (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ acpi_ex_exit_interpreter();
+ status =
+ acpi_tb_install_and_load_table(table, ACPI_PTR_TO_PHYSADDR(table),
+ ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL,
+ TRUE, &table_index);
+ acpi_ex_enter_interpreter();
if (ACPI_FAILURE(status)) {
/* Delete allocated table buffer */
@@ -491,25 +451,13 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
}
/*
- * Note: Now table is "INSTALLED", it must be validated before
- * loading.
- */
- status =
- acpi_tb_validate_table(&acpi_gbl_root_table_list.
- tables[table_index]);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
- /*
* Add the table to the namespace.
*
* Note: Load the table objects relative to the root of the namespace.
* This appears to go against the ACPI specification, but we do it for
* compatibility with other ACPI implementations.
*/
- status =
- acpi_ex_add_table(table_index, acpi_gbl_root_node, &ddb_handle);
+ status = acpi_ex_add_table(table_index, &ddb_handle);
if (ACPI_FAILURE(status)) {
/* On error, table_ptr was deallocated above */
@@ -532,14 +480,6 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
/* Remove the reference by added by acpi_ex_store above */
acpi_ut_remove_reference(ddb_handle);
-
- /* Invoke table handler if present */
-
- if (acpi_gbl_table_handler) {
- (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
- acpi_gbl_table_handler_context);
- }
-
return_ACPI_STATUS(status);
}
@@ -592,10 +532,17 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
table_index = table_desc->reference.value;
+ /*
+ * Release the interpreter lock so that the table lock won't have
+ * strict order requirement against it.
+ */
+ acpi_ex_exit_interpreter();
+
/* Ensure the table is still loaded */
if (!acpi_tb_is_table_loaded(table_index)) {
- return_ACPI_STATUS(AE_NOT_EXIST);
+ status = AE_NOT_EXIST;
+ goto lock_and_exit;
}
/* Invoke table handler if present */
@@ -613,16 +560,24 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
status = acpi_tb_delete_namespace_by_owner(table_index);
if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
+ goto lock_and_exit;
}
(void)acpi_tb_release_owner_id(table_index);
acpi_tb_set_table_loaded_flag(table_index, FALSE);
+lock_and_exit:
+
+ /* Re-acquire the interpreter lock */
+
+ acpi_ex_enter_interpreter();
+
/*
* Invalidate the handle. We do this because the handle may be stored
* in a named object and may not be actually deleted until much later.
*/
- ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID;
- return_ACPI_STATUS(AE_OK);
+ if (ACPI_SUCCESS(status)) {
+ ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID;
+ }
+ return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c
index b7e9b3d..588ad14 100644
--- a/drivers/acpi/acpica/exconvrt.c
+++ b/drivers/acpi/acpica/exconvrt.c
@@ -124,9 +124,9 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
* of ACPI 3.0) is that the to_integer() operator allows both decimal
* and hexadecimal strings (hex prefixed with "0x").
*/
- status = acpi_ut_strtoul64((char *)pointer, flags,
- acpi_gbl_integer_byte_width,
- &result);
+ status = acpi_ut_strtoul64(ACPI_CAST_PTR(char, pointer),
+ (acpi_gbl_integer_byte_width |
+ flags), &result);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -632,7 +632,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type,
*/
status =
acpi_ex_convert_to_integer(source_desc, result_desc,
- 16);
+ ACPI_STRTOUL_BASE16);
break;
case ACPI_TYPE_STRING:
diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c
index 4f7e667..37c88b4 100644
--- a/drivers/acpi/acpica/exmisc.c
+++ b/drivers/acpi/acpica/exmisc.c
@@ -327,8 +327,8 @@ acpi_ex_do_logical_op(u16 opcode,
switch (operand0->common.type) {
case ACPI_TYPE_INTEGER:
- status =
- acpi_ex_convert_to_integer(operand1, &local_operand1, 16);
+ status = acpi_ex_convert_to_integer(operand1, &local_operand1,
+ ACPI_STRTOUL_BASE16);
break;
case ACPI_TYPE_STRING:
diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c
index 4e17506..0073004 100644
--- a/drivers/acpi/acpica/exoparg1.c
+++ b/drivers/acpi/acpica/exoparg1.c
@@ -521,9 +521,10 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state)
case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */
+ /* Perform "explicit" conversion */
+
status =
- acpi_ex_convert_to_integer(operand[0], &return_desc,
- ACPI_ANY_BASE);
+ acpi_ex_convert_to_integer(operand[0], &return_desc, 0);
if (return_desc == operand[0]) {
/* No conversion performed, add ref to handle return value */
@@ -890,14 +891,16 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
* Field, so we need to resolve the node to a value.
*/
status =
- acpi_ns_get_node(walk_state->scope_info->
- scope.node,
- operand[0]->string.pointer,
- ACPI_NS_SEARCH_PARENT,
- ACPI_CAST_INDIRECT_PTR
- (struct
- acpi_namespace_node,
- &return_desc));
+ acpi_ns_get_node_unlocked(walk_state->
+ scope_info->scope.
+ node,
+ operand[0]->
+ string.pointer,
+ ACPI_NS_SEARCH_PARENT,
+ ACPI_CAST_INDIRECT_PTR
+ (struct
+ acpi_namespace_node,
+ &return_desc));
if (ACPI_FAILURE(status)) {
goto cleanup;
}
diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c
index 27b41fd..f29eba1 100644
--- a/drivers/acpi/acpica/exresop.c
+++ b/drivers/acpi/acpica/exresop.c
@@ -410,12 +410,13 @@ acpi_ex_resolve_operands(u16 opcode,
case ARGI_INTEGER:
/*
- * Need an operand of type ACPI_TYPE_INTEGER,
- * But we can implicitly convert from a STRING or BUFFER
- * aka - "Implicit Source Operand Conversion"
+ * Need an operand of type ACPI_TYPE_INTEGER, but we can
+ * implicitly convert from a STRING or BUFFER.
+ *
+ * Known as "Implicit Source Operand Conversion"
*/
- status =
- acpi_ex_convert_to_integer(obj_desc, stack_ptr, 16);
+ status = acpi_ex_convert_to_integer(obj_desc, stack_ptr,
+ ACPI_STRTOUL_BASE16);
if (ACPI_FAILURE(status)) {
if (status == AE_TYPE) {
ACPI_ERROR((AE_INFO,
diff --git a/drivers/acpi/acpica/extrace.c b/drivers/acpi/acpica/extrace.c
index b52e848..c9ca826 100644
--- a/drivers/acpi/acpica/extrace.c
+++ b/drivers/acpi/acpica/extrace.c
@@ -201,7 +201,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{
- acpi_status status;
char *pathname = NULL;
u8 enabled = FALSE;
@@ -211,11 +210,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
}
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit;
- }
-
enabled = acpi_ex_interpreter_trace_enabled(pathname);
if (enabled && !acpi_gbl_trace_method_object) {
acpi_gbl_trace_method_object = obj_desc;
@@ -233,9 +227,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
}
}
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
-exit:
if (enabled) {
ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, TRUE,
obj_desc ? obj_desc->method.aml_start : NULL,
@@ -267,7 +258,6 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state)
{
- acpi_status status;
char *pathname = NULL;
u8 enabled;
@@ -277,26 +267,14 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
}
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit_path;
- }
-
enabled = acpi_ex_interpreter_trace_enabled(NULL);
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
if (enabled) {
ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, FALSE,
obj_desc ? obj_desc->method.aml_start : NULL,
pathname);
}
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto exit_path;
- }
-
/* Check whether the tracer should be stopped */
if (acpi_gbl_trace_method_object == obj_desc) {
@@ -312,9 +290,6 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
acpi_gbl_trace_method_object = NULL;
}
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
-exit_path:
if (pathname) {
ACPI_FREE(pathname);
}
diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c
index 425f133..a8b857a 100644
--- a/drivers/acpi/acpica/exutils.c
+++ b/drivers/acpi/acpica/exutils.c
@@ -94,6 +94,10 @@ void acpi_ex_enter_interpreter(void)
ACPI_ERROR((AE_INFO,
"Could not acquire AML Interpreter mutex"));
}
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO, "Could not acquire AML Namespace mutex"));
+ }
return_VOID;
}
@@ -127,6 +131,10 @@ void acpi_ex_exit_interpreter(void)
ACPI_FUNCTION_TRACE(ex_exit_interpreter);
+ status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ ACPI_ERROR((AE_INFO, "Could not release AML Namespace mutex"));
+ }
status = acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO,
diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c
index bdecd5e..76b0e35 100644
--- a/drivers/acpi/acpica/hwgpe.c
+++ b/drivers/acpi/acpica/hwgpe.c
@@ -98,7 +98,7 @@ acpi_status
acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
{
struct acpi_gpe_register_info *gpe_register_info;
- acpi_status status;
+ acpi_status status = AE_OK;
u32 enable_mask;
u32 register_bit;
@@ -148,9 +148,14 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
return (AE_BAD_PARAMETER);
}
- /* Write the updated enable mask */
+ if (!(register_bit & gpe_register_info->mask_for_run)) {
- status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address);
+ /* Write the updated enable mask */
+
+ status =
+ acpi_hw_write(enable_mask,
+ &gpe_register_info->enable_address);
+ }
return (status);
}
@@ -242,6 +247,12 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info,
local_event_status |= ACPI_EVENT_FLAG_ENABLED;
}
+ /* GPE currently masked? (masked for runtime?) */
+
+ if (register_bit & gpe_register_info->mask_for_run) {
+ local_event_status |= ACPI_EVENT_FLAG_MASKED;
+ }
+
/* GPE enabled for wake? */
if (register_bit & gpe_register_info->enable_for_wake) {
@@ -397,6 +408,7 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
u32 i;
acpi_status status;
struct acpi_gpe_register_info *gpe_register_info;
+ u8 enable_mask;
/* NOTE: assumes that all GPEs are currently disabled */
@@ -410,9 +422,10 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
/* Enable all "runtime" GPEs in this register */
+ enable_mask = gpe_register_info->enable_for_run &
+ ~gpe_register_info->mask_for_run;
status =
- acpi_hw_gpe_enable_write(gpe_register_info->enable_for_run,
- gpe_register_info);
+ acpi_hw_gpe_enable_write(enable_mask, gpe_register_info);
if (ACPI_FAILURE(status)) {
return (status);
}
diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c
index 426a630..73f98d3 100644
--- a/drivers/acpi/acpica/nsaccess.c
+++ b/drivers/acpi/acpica/nsaccess.c
@@ -108,9 +108,9 @@ acpi_status acpi_ns_root_initialize(void)
}
status =
- acpi_ns_lookup(NULL, (char *)init_val->name, init_val->type,
- ACPI_IMODE_LOAD_PASS2, ACPI_NS_NO_UPSEARCH,
- NULL, &new_node);
+ acpi_ns_lookup(NULL, ACPI_CAST_PTR(char, init_val->name),
+ init_val->type, ACPI_IMODE_LOAD_PASS2,
+ ACPI_NS_NO_UPSEARCH, NULL, &new_node);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"Could not create predefined name %s",
diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c
index c803bda..2b85dee 100644
--- a/drivers/acpi/acpica/nsconvert.c
+++ b/drivers/acpi/acpica/nsconvert.c
@@ -79,7 +79,6 @@ acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
/* String-to-Integer conversion */
status = acpi_ut_strtoul64(original_object->string.pointer,
- ACPI_ANY_BASE,
acpi_gbl_integer_byte_width, &value);
if (ACPI_FAILURE(status)) {
return (status);
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
index ce1f860..84f35dd 100644
--- a/drivers/acpi/acpica/nsdump.c
+++ b/drivers/acpi/acpica/nsdump.c
@@ -338,7 +338,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
case ACPI_TYPE_STRING:
acpi_os_printf("Len %.2X ", obj_desc->string.length);
- acpi_ut_print_string(obj_desc->string.pointer, 32);
+ acpi_ut_print_string(obj_desc->string.pointer, 80);
acpi_os_printf("\n");
break;
diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c
index b5e2b0a..334d3c5 100644
--- a/drivers/acpi/acpica/nsload.c
+++ b/drivers/acpi/acpica/nsload.c
@@ -46,6 +46,7 @@
#include "acnamesp.h"
#include "acdispat.h"
#include "actables.h"
+#include "acinterp.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsload")
@@ -78,20 +79,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
ACPI_FUNCTION_TRACE(ns_load_table);
- /*
- * Parse the table and load the namespace with all named
- * objects found within. Control methods are NOT parsed
- * at this time. In fact, the control methods cannot be
- * parsed until the entire namespace is loaded, because
- * if a control method makes a forward reference (call)
- * to another control method, we can't continue parsing
- * because we don't know how many arguments to parse next!
- */
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
/* If table already loaded into namespace, just return */
if (acpi_tb_is_table_loaded(table_index)) {
@@ -107,6 +94,15 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
goto unlock;
}
+ /*
+ * Parse the table and load the namespace with all named
+ * objects found within. Control methods are NOT parsed
+ * at this time. In fact, the control methods cannot be
+ * parsed until the entire namespace is loaded, because
+ * if a control method makes a forward reference (call)
+ * to another control method, we can't continue parsing
+ * because we don't know how many arguments to parse next!
+ */
status = acpi_ns_parse_table(table_index, node);
if (ACPI_SUCCESS(status)) {
acpi_tb_set_table_loaded_flag(table_index, TRUE);
@@ -120,7 +116,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
* exist. This target of Scope must already exist in the
* namespace, as per the ACPI specification.
*/
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
acpi_ns_delete_namespace_by_owner(acpi_gbl_root_table_list.
tables[table_index].owner_id);
@@ -129,8 +124,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
}
unlock:
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -162,7 +155,8 @@ unlock:
* other ACPI implementations. Optionally, the execution can be deferred
* until later, see acpi_initialize_objects.
*/
- if (!acpi_gbl_group_module_level_code) {
+ if (!acpi_gbl_parse_table_as_term_list
+ && !acpi_gbl_group_module_level_code) {
acpi_ns_exec_module_code_list();
}
diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c
index f631a47..4f14e92 100644
--- a/drivers/acpi/acpica/nsparse.c
+++ b/drivers/acpi/acpica/nsparse.c
@@ -47,12 +47,103 @@
#include "acparser.h"
#include "acdispat.h"
#include "actables.h"
+#include "acinterp.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsparse")
/*******************************************************************************
*
+ * FUNCTION: ns_execute_table
+ *
+ * PARAMETERS: table_desc - An ACPI table descriptor for table to parse
+ * start_node - Where to enter the table into the namespace
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load ACPI/AML table by executing the entire table as a
+ * term_list.
+ *
+ ******************************************************************************/
+acpi_status
+acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node)
+{
+ acpi_status status;
+ struct acpi_table_header *table;
+ acpi_owner_id owner_id;
+ struct acpi_evaluate_info *info = NULL;
+ u32 aml_length;
+ u8 *aml_start;
+ union acpi_operand_object *method_obj = NULL;
+
+ ACPI_FUNCTION_TRACE(ns_execute_table);
+
+ status = acpi_get_table_by_index(table_index, &table);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Table must consist of at least a complete header */
+
+ if (table->length < sizeof(struct acpi_table_header)) {
+ return_ACPI_STATUS(AE_BAD_HEADER);
+ }
+
+ aml_start = (u8 *)table + sizeof(struct acpi_table_header);
+ aml_length = table->length - sizeof(struct acpi_table_header);
+
+ status = acpi_tb_get_owner_id(table_index, &owner_id);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /* Create, initialize, and link a new temporary method object */
+
+ method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
+ if (!method_obj) {
+ return_ACPI_STATUS(AE_NO_MEMORY);
+ }
+
+ /* Allocate the evaluation information block */
+
+ info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
+ if (!info) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
+ "Create table code block: %p\n", method_obj));
+
+ method_obj->method.aml_start = aml_start;
+ method_obj->method.aml_length = aml_length;
+ method_obj->method.owner_id = owner_id;
+ method_obj->method.info_flags |= ACPI_METHOD_MODULE_LEVEL;
+
+ info->pass_number = ACPI_IMODE_EXECUTE;
+ info->node = start_node;
+ info->obj_desc = method_obj;
+ info->node_flags = info->node->flags;
+ info->full_pathname = acpi_ns_get_normalized_pathname(info->node, TRUE);
+ if (!info->full_pathname) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ status = acpi_ps_execute_table(info);
+
+cleanup:
+ if (info) {
+ ACPI_FREE(info->full_pathname);
+ info->full_pathname = NULL;
+ }
+ ACPI_FREE(info);
+ acpi_ut_remove_reference(method_obj);
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: ns_one_complete_parse
*
* PARAMETERS: pass_number - 1 or 2
@@ -63,6 +154,7 @@ ACPI_MODULE_NAME("nsparse")
* DESCRIPTION: Perform one complete parse of an ACPI/AML table.
*
******************************************************************************/
+
acpi_status
acpi_ns_one_complete_parse(u32 pass_number,
u32 table_index,
@@ -143,7 +235,9 @@ acpi_ns_one_complete_parse(u32 pass_number,
ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
"*PARSE* pass %u parse\n", pass_number));
+ acpi_ex_enter_interpreter();
status = acpi_ps_parse_aml(walk_state);
+ acpi_ex_exit_interpreter();
cleanup:
acpi_ps_delete_parse_tree(parse_root);
@@ -170,38 +264,47 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
ACPI_FUNCTION_TRACE(ns_parse_table);
- /*
- * AML Parse, pass 1
- *
- * In this pass, we load most of the namespace. Control methods
- * are not parsed until later. A parse tree is not created. Instead,
- * each Parser Op subtree is deleted when it is finished. This saves
- * a great deal of memory, and allows a small cache of parse objects
- * to service the entire parse. The second pass of the parse then
- * performs another complete parse of the AML.
- */
- ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 1\n"));
-
- status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1,
- table_index, start_node);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
+ if (acpi_gbl_parse_table_as_term_list) {
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start load pass\n"));
- /*
- * AML Parse, pass 2
- *
- * In this pass, we resolve forward references and other things
- * that could not be completed during the first pass.
- * Another complete parse of the AML is performed, but the
- * overhead of this is compensated for by the fact that the
- * parse objects are all cached.
- */
- ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 2\n"));
- status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2,
- table_index, start_node);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
+ status = acpi_ns_execute_table(table_index, start_node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+ } else {
+ /*
+ * AML Parse, pass 1
+ *
+ * In this pass, we load most of the namespace. Control methods
+ * are not parsed until later. A parse tree is not created.
+ * Instead, each Parser Op subtree is deleted when it is finished.
+ * This saves a great deal of memory, and allows a small cache of
+ * parse objects to service the entire parse. The second pass of
+ * the parse then performs another complete parse of the AML.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 1\n"));
+
+ status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1,
+ table_index, start_node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * AML Parse, pass 2
+ *
+ * In this pass, we resolve forward references and other things
+ * that could not be completed during the first pass.
+ * Another complete parse of the AML is performed, but the
+ * overhead of this is compensated for by the fact that the
+ * parse objects are all cached.
+ */
+ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 2\n"));
+ status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2,
+ table_index, start_node);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
}
return_ACPI_STATUS(status);
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
index 784a30b..691814d 100644
--- a/drivers/acpi/acpica/nsutils.c
+++ b/drivers/acpi/acpica/nsutils.c
@@ -662,7 +662,7 @@ u32 acpi_ns_opens_scope(acpi_object_type type)
/*******************************************************************************
*
- * FUNCTION: acpi_ns_get_node
+ * FUNCTION: acpi_ns_get_node_unlocked
*
* PARAMETERS: *pathname - Name to be found, in external (ASL) format. The
* \ (backslash) and ^ (carat) prefixes, and the
@@ -678,20 +678,21 @@ u32 acpi_ns_opens_scope(acpi_object_type type)
* DESCRIPTION: Look up a name relative to a given scope and return the
* corresponding Node. NOTE: Scope can be null.
*
- * MUTEX: Locks namespace
+ * MUTEX: Doesn't locks namespace
*
******************************************************************************/
acpi_status
-acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
- const char *pathname,
- u32 flags, struct acpi_namespace_node **return_node)
+acpi_ns_get_node_unlocked(struct acpi_namespace_node *prefix_node,
+ const char *pathname,
+ u32 flags, struct acpi_namespace_node **return_node)
{
union acpi_generic_state scope_info;
acpi_status status;
char *internal_path;
- ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname));
+ ACPI_FUNCTION_TRACE_PTR(ns_get_node_unlocked,
+ ACPI_CAST_PTR(char, pathname));
/* Simplest case is a null pathname */
@@ -718,13 +719,6 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
return_ACPI_STATUS(status);
}
- /* Must lock namespace during lookup */
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- goto cleanup;
- }
-
/* Setup lookup scope (search starting point) */
scope_info.scope.node = prefix_node;
@@ -740,9 +734,49 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
pathname, acpi_format_exception(status)));
}
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
-
-cleanup:
ACPI_FREE(internal_path);
return_ACPI_STATUS(status);
}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_get_node
+ *
+ * PARAMETERS: *pathname - Name to be found, in external (ASL) format. The
+ * \ (backslash) and ^ (carat) prefixes, and the
+ * . (period) to separate segments are supported.
+ * prefix_node - Root of subtree to be searched, or NS_ALL for the
+ * root of the name space. If Name is fully
+ * qualified (first s8 is '\'), the passed value
+ * of Scope will not be accessed.
+ * flags - Used to indicate whether to perform upsearch or
+ * not.
+ * return_node - Where the Node is returned
+ *
+ * DESCRIPTION: Look up a name relative to a given scope and return the
+ * corresponding Node. NOTE: Scope can be null.
+ *
+ * MUTEX: Locks namespace
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
+ const char *pathname,
+ u32 flags, struct acpi_namespace_node **return_node)
+{
+ acpi_status status;
+
+ ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname));
+
+ status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ns_get_node_unlocked(prefix_node, pathname,
+ flags, return_node);
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c
index 0a23897..1ce26d9 100644
--- a/drivers/acpi/acpica/psparse.c
+++ b/drivers/acpi/acpica/psparse.c
@@ -537,9 +537,11 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
/* Either the method parse or actual execution failed */
+ acpi_ex_exit_interpreter();
ACPI_ERROR_METHOD("Method parse/execution failed",
walk_state->method_node, NULL,
status);
+ acpi_ex_enter_interpreter();
/* Check for possible multi-thread reentrancy problem */
@@ -571,7 +573,9 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
* cleanup to do
*/
if (((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) ==
- ACPI_PARSE_EXECUTE) || (ACPI_FAILURE(status))) {
+ ACPI_PARSE_EXECUTE &&
+ !(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) ||
+ (ACPI_FAILURE(status))) {
acpi_ds_terminate_control_method(walk_state->
method_desc,
walk_state);
diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c
index cf30cd82..f3c8726 100644
--- a/drivers/acpi/acpica/psxface.c
+++ b/drivers/acpi/acpica/psxface.c
@@ -252,6 +252,90 @@ cleanup:
/*******************************************************************************
*
+ * FUNCTION: acpi_ps_execute_table
+ *
+ * PARAMETERS: info - Method info block, contains:
+ * node - Node to where the is entered into the
+ * namespace
+ * obj_desc - Pseudo method object describing the AML
+ * code of the entire table
+ * pass_number - Parse or execute pass
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Execute a table
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info)
+{
+ acpi_status status;
+ union acpi_parse_object *op = NULL;
+ struct acpi_walk_state *walk_state = NULL;
+
+ ACPI_FUNCTION_TRACE(ps_execute_table);
+
+ /* Create and init a Root Node */
+
+ op = acpi_ps_create_scope_op(info->obj_desc->method.aml_start);
+ if (!op) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ /* Create and initialize a new walk state */
+
+ walk_state =
+ acpi_ds_create_walk_state(info->obj_desc->method.owner_id, NULL,
+ NULL, NULL);
+ if (!walk_state) {
+ status = AE_NO_MEMORY;
+ goto cleanup;
+ }
+
+ status = acpi_ds_init_aml_walk(walk_state, op, info->node,
+ info->obj_desc->method.aml_start,
+ info->obj_desc->method.aml_length, info,
+ info->pass_number);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+
+ if (info->obj_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) {
+ walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL;
+ }
+
+ /* Info->Node is the default location to load the table */
+
+ if (info->node && info->node != acpi_gbl_root_node) {
+ status =
+ acpi_ds_scope_stack_push(info->node, ACPI_TYPE_METHOD,
+ walk_state);
+ if (ACPI_FAILURE(status)) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Parse the AML, walk_state will be deleted by parse_aml
+ */
+ acpi_ex_enter_interpreter();
+ status = acpi_ps_parse_aml(walk_state);
+ acpi_ex_exit_interpreter();
+ walk_state = NULL;
+
+cleanup:
+ if (walk_state) {
+ acpi_ds_delete_walk_state(walk_state);
+ }
+ if (op) {
+ acpi_ps_delete_parse_tree(op);
+ }
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ps_update_parameter_list
*
* PARAMETERS: info - See struct acpi_evaluate_info
diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c
index 1388a19..d9ca8c2 100644
--- a/drivers/acpi/acpica/tbdata.c
+++ b/drivers/acpi/acpica/tbdata.c
@@ -45,6 +45,7 @@
#include "accommon.h"
#include "acnamesp.h"
#include "actables.h"
+#include "acevents.h"
#define _COMPONENT ACPI_TABLES
ACPI_MODULE_NAME("tbdata")
@@ -613,17 +614,12 @@ acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index)
* lock may block, and also since the execution of a namespace walk
* must be allowed to use the interpreter.
*/
- (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock);
-
- acpi_ns_delete_namespace_by_owner(owner_id);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
-
+ acpi_ns_delete_namespace_by_owner(owner_id);
acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock);
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
return_ACPI_STATUS(status);
}
@@ -771,3 +767,142 @@ void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded)
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_load_table
+ *
+ * PARAMETERS: table_index - Table index
+ * parent_node - Where table index is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Load an ACPI table
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node)
+{
+ struct acpi_table_header *table;
+ acpi_status status;
+ acpi_owner_id owner_id;
+
+ ACPI_FUNCTION_TRACE(tb_load_table);
+
+ /*
+ * Note: Now table is "INSTALLED", it must be validated before
+ * using.
+ */
+ status = acpi_get_table_by_index(table_index, &table);
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ status = acpi_ns_load_table(table_index, parent_node);
+
+ /* Execute any module-level code that was found in the table */
+
+ if (!acpi_gbl_parse_table_as_term_list
+ && acpi_gbl_group_module_level_code) {
+ acpi_ns_exec_module_code_list();
+ }
+
+ /*
+ * Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is
+ * responsible for discovering any new wake GPEs by running _PRW methods
+ * that may have been loaded by this table.
+ */
+ status = acpi_tb_get_owner_id(table_index, &owner_id);
+ if (ACPI_SUCCESS(status)) {
+ acpi_ev_update_gpes(owner_id);
+ }
+
+ /* Invoke table handler if present */
+
+ if (acpi_gbl_table_handler) {
+ (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
+ acpi_gbl_table_handler_context);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_tb_install_and_load_table
+ *
+ * PARAMETERS: table - Pointer to the table
+ * address - Physical address of the table
+ * flags - Allocation flags of the table
+ * table_index - Where table index is returned
+ *
+ * RETURN: Status
+ *
+ * DESCRIPTION: Install and load an ACPI table
+ *
+ ******************************************************************************/
+
+acpi_status
+acpi_tb_install_and_load_table(struct acpi_table_header *table,
+ acpi_physical_address address,
+ u8 flags, u8 override, u32 *table_index)
+{
+ acpi_status status;
+ u32 i;
+ acpi_owner_id owner_id;
+
+ ACPI_FUNCTION_TRACE(acpi_load_table);
+
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+ /* Install the table and load it into the namespace */
+
+ status = acpi_tb_install_standard_table(address, flags, TRUE,
+ override, &i);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+
+ /*
+ * Note: Now table is "INSTALLED", it must be validated before
+ * using.
+ */
+ status = acpi_tb_validate_table(&acpi_gbl_root_table_list.tables[i]);
+ if (ACPI_FAILURE(status)) {
+ goto unlock_and_exit;
+ }
+
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ status = acpi_ns_load_table(i, acpi_gbl_root_node);
+
+ /* Execute any module-level code that was found in the table */
+
+ if (!acpi_gbl_parse_table_as_term_list
+ && acpi_gbl_group_module_level_code) {
+ acpi_ns_exec_module_code_list();
+ }
+
+ /*
+ * Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is
+ * responsible for discovering any new wake GPEs by running _PRW methods
+ * that may have been loaded by this table.
+ */
+ status = acpi_tb_get_owner_id(i, &owner_id);
+ if (ACPI_SUCCESS(status)) {
+ acpi_ev_update_gpes(owner_id);
+ }
+
+ /* Invoke table handler if present */
+
+ if (acpi_gbl_table_handler) {
+ (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
+ acpi_gbl_table_handler_context);
+ }
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
+
+unlock_and_exit:
+ *table_index = i;
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
+}
diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c
index 6208069..046c4d0 100644
--- a/drivers/acpi/acpica/tbfadt.c
+++ b/drivers/acpi/acpica/tbfadt.c
@@ -344,23 +344,27 @@ void acpi_tb_parse_fadt(void)
/* Obtain the DSDT and FACS tables via their addresses within the FADT */
- acpi_tb_install_fixed_table((acpi_physical_address)acpi_gbl_FADT.Xdsdt,
- ACPI_SIG_DSDT, &acpi_gbl_dsdt_index);
+ acpi_tb_install_standard_table((acpi_physical_address)acpi_gbl_FADT.
+ Xdsdt,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+ FALSE, TRUE, &acpi_gbl_dsdt_index);
/* If Hardware Reduced flag is set, there is no FACS */
if (!acpi_gbl_reduced_hardware) {
if (acpi_gbl_FADT.facs) {
- acpi_tb_install_fixed_table((acpi_physical_address)
- acpi_gbl_FADT.facs,
- ACPI_SIG_FACS,
- &acpi_gbl_facs_index);
+ acpi_tb_install_standard_table((acpi_physical_address)
+ acpi_gbl_FADT.facs,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+ FALSE, TRUE,
+ &acpi_gbl_facs_index);
}
if (acpi_gbl_FADT.Xfacs) {
- acpi_tb_install_fixed_table((acpi_physical_address)
- acpi_gbl_FADT.Xfacs,
- ACPI_SIG_FACS,
- &acpi_gbl_xfacs_index);
+ acpi_tb_install_standard_table((acpi_physical_address)
+ acpi_gbl_FADT.Xfacs,
+ ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
+ FALSE, TRUE,
+ &acpi_gbl_xfacs_index);
}
}
}
@@ -476,17 +480,19 @@ static void acpi_tb_convert_fadt(void)
u32 i;
/*
- * For ACPI 1.0 FADTs (revision 1 or 2), ensure that reserved fields which
+ * For ACPI 1.0 FADTs (revision 1), ensure that reserved fields which
* should be zero are indeed zero. This will workaround BIOSs that
* inadvertently place values in these fields.
*
* The ACPI 1.0 reserved fields that will be zeroed are the bytes located
* at offset 45, 55, 95, and the word located at offset 109, 110.
*
- * Note: The FADT revision value is unreliable. Only the length can be
- * trusted.
+ * Note: The FADT revision value is unreliable because of BIOS errors.
+ * The table length is instead used as the final word on the version.
+ *
+ * Note: FADT revision 3 is the ACPI 2.0 version of the FADT.
*/
- if (acpi_gbl_FADT.header.length <= ACPI_FADT_V2_SIZE) {
+ if (acpi_gbl_FADT.header.length <= ACPI_FADT_V3_SIZE) {
acpi_gbl_FADT.preferred_profile = 0;
acpi_gbl_FADT.pstate_control = 0;
acpi_gbl_FADT.cst_control = 0;
@@ -552,78 +558,74 @@ static void acpi_tb_convert_fadt(void)
*
* Address32 zero, Address64 [don't care] - Use Address64
*
+ * No override: if acpi_gbl_use32_bit_fadt_addresses is FALSE, and:
* Address32 non-zero, Address64 zero - Copy/use Address32
* Address32 non-zero == Address64 non-zero - Use Address64
* Address32 non-zero != Address64 non-zero - Warning, use Address64
*
* Override: if acpi_gbl_use32_bit_fadt_addresses is TRUE, and:
+ * Address32 non-zero, Address64 zero - Copy/use Address32
+ * Address32 non-zero == Address64 non-zero - Copy/use Address32
* Address32 non-zero != Address64 non-zero - Warning, copy/use Address32
*
* Note: space_id is always I/O for 32-bit legacy address fields
*/
if (address32) {
- if (!address64->address) {
+ if (address64->address) {
+ if (address64->address != (u64)address32) {
+
+ /* Address mismatch */
+
+ ACPI_BIOS_WARNING((AE_INFO,
+ "32/64X address mismatch in FADT/%s: "
+ "0x%8.8X/0x%8.8X%8.8X, using %u-bit address",
+ name, address32,
+ ACPI_FORMAT_UINT64
+ (address64->address),
+ acpi_gbl_use32_bit_fadt_addresses
+ ? 32 : 64));
+ }
- /* 64-bit address is zero, use 32-bit address */
+ /*
+ * For each extended field, check for length mismatch
+ * between the legacy length field and the corresponding
+ * 64-bit X length field.
+ * Note: If the legacy length field is > 0xFF bits, ignore
+ * this check. (GPE registers can be larger than the
+ * 64-bit GAS structure can accomodate, 0xFF bits).
+ */
+ if ((ACPI_MUL_8(length) <= ACPI_UINT8_MAX) &&
+ (address64->bit_width !=
+ ACPI_MUL_8(length))) {
+ ACPI_BIOS_WARNING((AE_INFO,
+ "32/64X length mismatch in FADT/%s: %u/%u",
+ name,
+ ACPI_MUL_8(length),
+ address64->
+ bit_width));
+ }
+ }
+ /*
+ * Hardware register access code always uses the 64-bit fields.
+ * So if the 64-bit field is zero or is to be overridden,
+ * initialize it with the 32-bit fields.
+ * Note that when the 32-bit address favor is specified, the
+ * 64-bit fields are always re-initialized so that
+ * access_size/bit_width/bit_offset fields can be correctly
+ * configured to the values to trigger a 32-bit compatible
+ * access mode in the hardware register access code.
+ */
+ if (!address64->address
+ || acpi_gbl_use32_bit_fadt_addresses) {
acpi_tb_init_generic_address(address64,
ACPI_ADR_SPACE_SYSTEM_IO,
- *ACPI_ADD_PTR(u8,
- &acpi_gbl_FADT,
- fadt_info_table
- [i].
- length),
+ length,
(u64)address32,
name, flags);
- } else if (address64->address != (u64)address32) {
-
- /* Address mismatch */
-
- ACPI_BIOS_WARNING((AE_INFO,
- "32/64X address mismatch in FADT/%s: "
- "0x%8.8X/0x%8.8X%8.8X, using %u-bit address",
- name, address32,
- ACPI_FORMAT_UINT64
- (address64->address),
- acpi_gbl_use32_bit_fadt_addresses
- ? 32 : 64));
-
- if (acpi_gbl_use32_bit_fadt_addresses) {
-
- /* 32-bit address override */
-
- acpi_tb_init_generic_address(address64,
- ACPI_ADR_SPACE_SYSTEM_IO,
- *ACPI_ADD_PTR
- (u8,
- &acpi_gbl_FADT,
- fadt_info_table
- [i].
- length),
- (u64)
- address32,
- name,
- flags);
- }
}
}
- /*
- * For each extended field, check for length mismatch between the
- * legacy length field and the corresponding 64-bit X length field.
- * Note: If the legacy length field is > 0xFF bits, ignore this
- * check. (GPE registers can be larger than the 64-bit GAS structure
- * can accomodate, 0xFF bits).
- */
- if (address64->address &&
- (ACPI_MUL_8(length) <= ACPI_UINT8_MAX) &&
- (address64->bit_width != ACPI_MUL_8(length))) {
- ACPI_BIOS_WARNING((AE_INFO,
- "32/64X length mismatch in FADT/%s: %u/%u",
- name, ACPI_MUL_8(length),
- address64->bit_width));
- }
-
if (fadt_info_table[i].flags & ACPI_FADT_REQUIRED) {
/*
* Field is required (Pm1a_event, Pm1a_control).
diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c
index e348d61..f6b9b4e 100644
--- a/drivers/acpi/acpica/tbfind.c
+++ b/drivers/acpi/acpica/tbfind.c
@@ -68,7 +68,7 @@ acpi_status
acpi_tb_find_table(char *signature,
char *oem_id, char *oem_table_id, u32 *table_index)
{
- acpi_status status;
+ acpi_status status = AE_OK;
struct acpi_table_header header;
u32 i;
@@ -96,6 +96,7 @@ acpi_tb_find_table(char *signature,
/* Search for the table */
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
if (memcmp(&(acpi_gbl_root_table_list.tables[i].signature),
header.signature, ACPI_NAME_SIZE)) {
@@ -115,7 +116,7 @@ acpi_tb_find_table(char *signature,
acpi_tb_validate_table(&acpi_gbl_root_table_list.
tables[i]);
if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
+ goto unlock_and_exit;
}
if (!acpi_gbl_root_table_list.tables[i].pointer) {
@@ -144,9 +145,12 @@ acpi_tb_find_table(char *signature,
ACPI_DEBUG_PRINT((ACPI_DB_TABLES,
"Found table [%4.4s]\n",
header.signature));
- return_ACPI_STATUS(AE_OK);
+ goto unlock_and_exit;
}
}
+ status = AE_NOT_FOUND;
- return_ACPI_STATUS(AE_NOT_FOUND);
+unlock_and_exit:
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
+ return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c
index 8b13052..5fdf251 100644
--- a/drivers/acpi/acpica/tbinstal.c
+++ b/drivers/acpi/acpica/tbinstal.c
@@ -157,68 +157,6 @@ acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
/*******************************************************************************
*
- * FUNCTION: acpi_tb_install_fixed_table
- *
- * PARAMETERS: address - Physical address of DSDT or FACS
- * signature - Table signature, NULL if no need to
- * match
- * table_index - Where the table index is returned
- *
- * RETURN: Status
- *
- * DESCRIPTION: Install a fixed ACPI table (DSDT/FACS) into the global data
- * structure.
- *
- ******************************************************************************/
-
-acpi_status
-acpi_tb_install_fixed_table(acpi_physical_address address,
- char *signature, u32 *table_index)
-{
- struct acpi_table_desc new_table_desc;
- acpi_status status;
-
- ACPI_FUNCTION_TRACE(tb_install_fixed_table);
-
- if (!address) {
- ACPI_ERROR((AE_INFO,
- "Null physical address for ACPI table [%s]",
- signature));
- return (AE_NO_MEMORY);
- }
-
- /* Fill a table descriptor for validation */
-
- status = acpi_tb_acquire_temp_table(&new_table_desc, address,
- ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL);
- if (ACPI_FAILURE(status)) {
- ACPI_ERROR((AE_INFO,
- "Could not acquire table length at %8.8X%8.8X",
- ACPI_FORMAT_UINT64(address)));
- return_ACPI_STATUS(status);
- }
-
- /* Validate and verify a table before installation */
-
- status = acpi_tb_verify_temp_table(&new_table_desc, signature);
- if (ACPI_FAILURE(status)) {
- goto release_and_exit;
- }
-
- /* Add the table to the global root table list */
-
- acpi_tb_install_table_with_override(&new_table_desc, TRUE, table_index);
-
-release_and_exit:
-
- /* Release the temporary table descriptor */
-
- acpi_tb_release_temp_table(&new_table_desc);
- return_ACPI_STATUS(status);
-}
-
-/*******************************************************************************
- *
* FUNCTION: acpi_tb_install_standard_table
*
* PARAMETERS: address - Address of the table (might be a virtual
@@ -230,8 +168,7 @@ release_and_exit:
*
* RETURN: Status
*
- * DESCRIPTION: This function is called to install an ACPI table that is
- * neither DSDT nor FACS (a "standard" table.)
+ * DESCRIPTION: This function is called to verify and install an ACPI table.
* When this function is called by "Load" or "LoadTable" opcodes,
* or by acpi_load_table() API, the "Reload" parameter is set.
* After sucessfully returning from this function, table is
@@ -364,6 +301,14 @@ acpi_tb_install_standard_table(acpi_physical_address address,
acpi_tb_install_table_with_override(&new_table_desc, override,
table_index);
+ /* Invoke table handler if present */
+
+ if (acpi_gbl_table_handler) {
+ (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_INSTALL,
+ new_table_desc.pointer,
+ acpi_gbl_table_handler_context);
+ }
+
release_and_exit:
/* Release the temporary table descriptor */
diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c
index e285539..51eb07c 100644
--- a/drivers/acpi/acpica/tbutils.c
+++ b/drivers/acpi/acpica/tbutils.c
@@ -252,7 +252,8 @@ acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size)
*
******************************************************************************/
-acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
+acpi_status ACPI_INIT_FUNCTION
+acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
{
struct acpi_table_rsdp *rsdp;
u32 table_entry_size;
diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c
index 3ecec93..4ab6b9c 100644
--- a/drivers/acpi/acpica/tbxface.c
+++ b/drivers/acpi/acpica/tbxface.c
@@ -98,7 +98,7 @@ acpi_status acpi_allocate_root_table(u32 initial_table_count)
*
******************************************************************************/
-acpi_status __init
+acpi_status ACPI_INIT_FUNCTION
acpi_initialize_tables(struct acpi_table_desc *initial_table_array,
u32 initial_table_count, u8 allow_resize)
{
@@ -164,7 +164,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_tables)
* kernel.
*
******************************************************************************/
-acpi_status __init acpi_reallocate_root_table(void)
+acpi_status ACPI_INIT_FUNCTION acpi_reallocate_root_table(void)
{
acpi_status status;
diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c
index ac71abc..5569f63 100644
--- a/drivers/acpi/acpica/tbxfload.c
+++ b/drivers/acpi/acpica/tbxfload.c
@@ -63,7 +63,7 @@ ACPI_MODULE_NAME("tbxfload")
* DESCRIPTION: Load the ACPI tables from the RSDT/XSDT
*
******************************************************************************/
-acpi_status __init acpi_load_tables(void)
+acpi_status ACPI_INIT_FUNCTION acpi_load_tables(void)
{
acpi_status status;
@@ -103,7 +103,8 @@ acpi_status __init acpi_load_tables(void)
"While loading namespace from ACPI tables"));
}
- if (!acpi_gbl_group_module_level_code) {
+ if (acpi_gbl_parse_table_as_term_list
+ || !acpi_gbl_group_module_level_code) {
/*
* Initialize the objects that remain uninitialized. This
* runs the executable AML that may be part of the
@@ -188,11 +189,11 @@ acpi_status acpi_tb_load_namespace(void)
memcpy(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT,
sizeof(struct acpi_table_header));
- (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
-
/* Load and parse tables */
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
status = acpi_ns_load_table(acpi_gbl_dsdt_index, acpi_gbl_root_node);
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "[DSDT] table load failed"));
tables_failed++;
@@ -202,7 +203,6 @@ acpi_status acpi_tb_load_namespace(void)
/* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */
- (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
table = &acpi_gbl_root_table_list.tables[i];
@@ -220,6 +220,7 @@ acpi_status acpi_tb_load_namespace(void)
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
status = acpi_ns_load_table(i, acpi_gbl_root_node);
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"(%4.4s:%8.8s) while loading table",
@@ -235,8 +236,6 @@ acpi_status acpi_tb_load_namespace(void)
} else {
tables_loaded++;
}
-
- (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
}
if (!tables_failed) {
@@ -272,7 +271,7 @@ unlock_and_exit:
*
******************************************************************************/
-acpi_status __init
+acpi_status ACPI_INIT_FUNCTION
acpi_install_table(acpi_physical_address address, u8 physical)
{
acpi_status status;
@@ -324,49 +323,13 @@ acpi_status acpi_load_table(struct acpi_table_header *table)
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
- /* Must acquire the interpreter lock during this operation */
-
- status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
- if (ACPI_FAILURE(status)) {
- return_ACPI_STATUS(status);
- }
-
/* Install the table and load it into the namespace */
ACPI_INFO(("Host-directed Dynamic ACPI Table Load:"));
- (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
-
- status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table),
- ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
- TRUE, FALSE, &table_index);
-
- (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
- if (ACPI_FAILURE(status)) {
- goto unlock_and_exit;
- }
-
- /*
- * Note: Now table is "INSTALLED", it must be validated before
- * using.
- */
status =
- acpi_tb_validate_table(&acpi_gbl_root_table_list.
- tables[table_index]);
- if (ACPI_FAILURE(status)) {
- goto unlock_and_exit;
- }
-
- status = acpi_ns_load_table(table_index, acpi_gbl_root_node);
-
- /* Invoke table handler if present */
-
- if (acpi_gbl_table_handler) {
- (void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
- acpi_gbl_table_handler_context);
- }
-
-unlock_and_exit:
- (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
+ acpi_tb_install_and_load_table(table, ACPI_PTR_TO_PHYSADDR(table),
+ ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
+ FALSE, &table_index);
return_ACPI_STATUS(status);
}
@@ -415,9 +378,9 @@ acpi_status acpi_unload_parent_table(acpi_handle object)
return_ACPI_STATUS(AE_TYPE);
}
- /* Must acquire the interpreter lock during this operation */
+ /* Must acquire the table lock during this operation */
- status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
+ status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
@@ -444,8 +407,10 @@ acpi_status acpi_unload_parent_table(acpi_handle object)
/* Ensure the table is actually loaded */
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
if (!acpi_tb_is_table_loaded(i)) {
status = AE_NOT_EXIST;
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
break;
}
@@ -471,10 +436,11 @@ acpi_status acpi_unload_parent_table(acpi_handle object)
status = acpi_tb_release_owner_id(i);
acpi_tb_set_table_loaded_flag(i, FALSE);
+ (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
break;
}
- (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
+ (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c
index adb6cfc..0adb1c7 100644
--- a/drivers/acpi/acpica/tbxfroot.c
+++ b/drivers/acpi/acpica/tbxfroot.c
@@ -142,7 +142,8 @@ acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp)
*
******************************************************************************/
-acpi_status __init acpi_find_root_pointer(acpi_physical_address *table_address)
+acpi_status ACPI_INIT_FUNCTION
+acpi_find_root_pointer(acpi_physical_address *table_address)
{
u8 *table_ptr;
u8 *mem_rover;
@@ -244,6 +245,8 @@ acpi_status __init acpi_find_root_pointer(acpi_physical_address *table_address)
return_ACPI_STATUS(AE_NOT_FOUND);
}
+ACPI_EXPORT_SYMBOL_INIT(acpi_find_root_pointer)
+
/*******************************************************************************
*
* FUNCTION: acpi_tb_scan_memory_for_rsdp
diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c
index c986ec6..433d822 100644
--- a/drivers/acpi/acpica/utaddress.c
+++ b/drivers/acpi/acpica/utaddress.c
@@ -77,7 +77,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id,
u32 length, struct acpi_namespace_node *region_node)
{
struct acpi_address_range *range_info;
- acpi_status status;
ACPI_FUNCTION_TRACE(ut_add_address_range);
@@ -97,12 +96,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id,
range_info->end_address = (address + length - 1);
range_info->region_node = region_node;
- status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
- if (ACPI_FAILURE(status)) {
- ACPI_FREE(range_info);
- return_ACPI_STATUS(status);
- }
-
range_info->next = acpi_gbl_address_range_list[space_id];
acpi_gbl_address_range_list[space_id] = range_info;
@@ -112,7 +105,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id,
ACPI_FORMAT_UINT64(address),
ACPI_FORMAT_UINT64(range_info->end_address)));
- (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(AE_OK);
}
diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c
index bd31faf..ff29812 100644
--- a/drivers/acpi/acpica/utbuffer.c
+++ b/drivers/acpi/acpica/utbuffer.c
@@ -239,8 +239,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
u8 buf_char;
if (!buffer) {
- acpi_ut_file_printf(file,
- "Null Buffer Pointer in DumpBuffer!\n");
+ fprintf(file, "Null Buffer Pointer in DumpBuffer!\n");
return;
}
@@ -254,7 +253,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
/* Print current offset */
- acpi_ut_file_printf(file, "%6.4X: ", (base_offset + i));
+ fprintf(file, "%6.4X: ", (base_offset + i));
/* Print 16 hex chars */
@@ -263,8 +262,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
/* Dump fill spaces */
- acpi_ut_file_printf(file, "%*s",
- ((display * 2) + 1), " ");
+ fprintf(file, "%*s", ((display * 2) + 1), " ");
j += display;
continue;
}
@@ -273,34 +271,34 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
case DB_BYTE_DISPLAY:
default: /* Default is BYTE display */
- acpi_ut_file_printf(file, "%02X ",
- buffer[(acpi_size)i + j]);
+ fprintf(file, "%02X ",
+ buffer[(acpi_size)i + j]);
break;
case DB_WORD_DISPLAY:
ACPI_MOVE_16_TO_32(&temp32,
&buffer[(acpi_size)i + j]);
- acpi_ut_file_printf(file, "%04X ", temp32);
+ fprintf(file, "%04X ", temp32);
break;
case DB_DWORD_DISPLAY:
ACPI_MOVE_32_TO_32(&temp32,
&buffer[(acpi_size)i + j]);
- acpi_ut_file_printf(file, "%08X ", temp32);
+ fprintf(file, "%08X ", temp32);
break;
case DB_QWORD_DISPLAY:
ACPI_MOVE_32_TO_32(&temp32,
&buffer[(acpi_size)i + j]);
- acpi_ut_file_printf(file, "%08X", temp32);
+ fprintf(file, "%08X", temp32);
ACPI_MOVE_32_TO_32(&temp32,
&buffer[(acpi_size)i + j +
4]);
- acpi_ut_file_printf(file, "%08X ", temp32);
+ fprintf(file, "%08X ", temp32);
break;
}
@@ -311,24 +309,24 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
* Print the ASCII equivalent characters but watch out for the bad
* unprintable ones (printable chars are 0x20 through 0x7E)
*/
- acpi_ut_file_printf(file, " ");
+ fprintf(file, " ");
for (j = 0; j < 16; j++) {
if (i + j >= count) {
- acpi_ut_file_printf(file, "\n");
+ fprintf(file, "\n");
return;
}
buf_char = buffer[(acpi_size)i + j];
if (isprint(buf_char)) {
- acpi_ut_file_printf(file, "%c", buf_char);
+ fprintf(file, "%c", buf_char);
} else {
- acpi_ut_file_printf(file, ".");
+ fprintf(file, ".");
}
}
/* Done with that line. */
- acpi_ut_file_printf(file, "\n");
+ fprintf(file, "\n");
i += 16;
}
diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c
index 5744222..044df9b 100644
--- a/drivers/acpi/acpica/utdebug.c
+++ b/drivers/acpi/acpica/utdebug.c
@@ -562,6 +562,43 @@ acpi_ut_ptr_exit(u32 line_number,
/*******************************************************************************
*
+ * FUNCTION: acpi_ut_str_exit
+ *
+ * PARAMETERS: line_number - Caller's line number
+ * function_name - Caller's procedure name
+ * module_name - Caller's module name
+ * component_id - Caller's component ID
+ * string - String to display
+ *
+ * RETURN: None
+ *
+ * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
+ * set in debug_level. Prints exit value also.
+ *
+ ******************************************************************************/
+
+void
+acpi_ut_str_exit(u32 line_number,
+ const char *function_name,
+ const char *module_name, u32 component_id, const char *string)
+{
+
+ /* Check if enabled up-front for performance */
+
+ if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
+ acpi_debug_print(ACPI_LV_FUNCTIONS,
+ line_number, function_name, module_name,
+ component_id, "%s %s\n",
+ acpi_gbl_function_exit_prefix, string);
+ }
+
+ if (acpi_gbl_nesting_level) {
+ acpi_gbl_nesting_level--;
+ }
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_trace_point
*
* PARAMETERS: type - Trace event type
@@ -591,27 +628,3 @@ acpi_trace_point(acpi_trace_event_type type, u8 begin, u8 *aml, char *pathname)
ACPI_EXPORT_SYMBOL(acpi_trace_point)
#endif
-#ifdef ACPI_APPLICATION
-/*******************************************************************************
- *
- * FUNCTION: acpi_log_error
- *
- * PARAMETERS: format - Printf format field
- * ... - Optional printf arguments
- *
- * RETURN: None
- *
- * DESCRIPTION: Print error message to the console, used by applications.
- *
- ******************************************************************************/
-void ACPI_INTERNAL_VAR_XFACE acpi_log_error(const char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
- (void)acpi_ut_file_vprintf(ACPI_FILE_ERR, format, args);
- va_end(args);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_log_error)
-#endif
diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c
index efd7988..15728ad 100644
--- a/drivers/acpi/acpica/utdecode.c
+++ b/drivers/acpi/acpica/utdecode.c
@@ -253,7 +253,7 @@ const char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc)
return_PTR("Invalid object");
}
- return_PTR(acpi_ut_get_type_name(obj_desc->common.type));
+ return_STR(acpi_ut_get_type_name(obj_desc->common.type));
}
/*******************************************************************************
diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c
index 4354fb8..36d2fc7 100644
--- a/drivers/acpi/acpica/uthex.c
+++ b/drivers/acpi/acpica/uthex.c
@@ -75,9 +75,41 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position)
/*******************************************************************************
*
+ * FUNCTION: acpi_ut_ascii_to_hex_byte
+ *
+ * PARAMETERS: two_ascii_chars - Pointer to two ASCII characters
+ * return_byte - Where converted byte is returned
+ *
+ * RETURN: Status and converted hex byte
+ *
+ * DESCRIPTION: Perform ascii-to-hex translation, exactly two ASCII characters
+ * to a single converted byte value.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte)
+{
+
+ /* Both ASCII characters must be valid hex digits */
+
+ if (!isxdigit((int)two_ascii_chars[0]) ||
+ !isxdigit((int)two_ascii_chars[1])) {
+ return (AE_BAD_HEX_CONSTANT);
+ }
+
+ *return_byte =
+ acpi_ut_ascii_char_to_hex(two_ascii_chars[1]) |
+ (acpi_ut_ascii_char_to_hex(two_ascii_chars[0]) << 4);
+
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ut_ascii_char_to_hex
*
- * PARAMETERS: hex_char - Hex character in Ascii
+ * PARAMETERS: hex_char - Hex character in Ascii. Must be:
+ * 0-9 or A-F or a-f
*
* RETURN: The binary value of the ascii/hex character
*
@@ -88,13 +120,19 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position)
u8 acpi_ut_ascii_char_to_hex(int hex_char)
{
- if (hex_char <= 0x39) {
- return ((u8)(hex_char - 0x30));
+ /* Values 0-9 */
+
+ if (hex_char <= '9') {
+ return ((u8)(hex_char - '0'));
}
- if (hex_char <= 0x46) {
+ /* Upper case A-F */
+
+ if (hex_char <= 'F') {
return ((u8)(hex_char - 0x37));
}
+ /* Lower case a-f */
+
return ((u8)(hex_char - 0x57));
}
diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c
index f91f724..1711fdf 100644
--- a/drivers/acpi/acpica/utinit.c
+++ b/drivers/acpi/acpica/utinit.c
@@ -206,7 +206,7 @@ acpi_status acpi_ut_init_globals(void)
acpi_gbl_next_owner_id_offset = 0;
acpi_gbl_debugger_configuration = DEBUGGER_THREADING;
acpi_gbl_osi_mutex = NULL;
- acpi_gbl_max_loop_iterations = 0xFFFF;
+ acpi_gbl_max_loop_iterations = ACPI_MAX_LOOP_COUNT;
/* Hardware oriented */
diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c
index 3465fe2..2514239 100644
--- a/drivers/acpi/acpica/utnonansi.c
+++ b/drivers/acpi/acpica/utnonansi.c
@@ -48,8 +48,8 @@
ACPI_MODULE_NAME("utnonansi")
/*
- * Non-ANSI C library functions - strlwr, strupr, stricmp, and a 64-bit
- * version of strtoul.
+ * Non-ANSI C library functions - strlwr, strupr, stricmp, and "safe"
+ * string functions.
*/
/*******************************************************************************
*
@@ -200,356 +200,3 @@ acpi_ut_safe_strncat(char *dest,
return (FALSE);
}
#endif
-
-/*******************************************************************************
- *
- * FUNCTION: acpi_ut_strtoul64
- *
- * PARAMETERS: string - Null terminated string
- * base - Radix of the string: 16 or 10 or
- * ACPI_ANY_BASE
- * max_integer_byte_width - Maximum allowable integer,in bytes:
- * 4 or 8 (32 or 64 bits)
- * ret_integer - Where the converted integer is
- * returned
- *
- * RETURN: Status and Converted value
- *
- * DESCRIPTION: Convert a string into an unsigned value. Performs either a
- * 32-bit or 64-bit conversion, depending on the input integer
- * size (often the current mode of the interpreter).
- *
- * NOTES: Negative numbers are not supported, as they are not supported
- * by ACPI.
- *
- * acpi_gbl_integer_byte_width should be set to the proper width.
- * For the core ACPICA code, this width depends on the DSDT
- * version. For iASL, the default byte width is always 8 for the
- * parser, but error checking is performed later to flag cases
- * where a 64-bit constant is defined in a 32-bit DSDT/SSDT.
- *
- * Does not support Octal strings, not needed at this time.
- *
- ******************************************************************************/
-
-acpi_status
-acpi_ut_strtoul64(char *string,
- u32 base, u32 max_integer_byte_width, u64 *ret_integer)
-{
- u32 this_digit = 0;
- u64 return_value = 0;
- u64 quotient;
- u64 dividend;
- u8 valid_digits = 0;
- u8 sign_of0x = 0;
- u8 term = 0;
-
- ACPI_FUNCTION_TRACE_STR(ut_strtoul64, string);
-
- switch (base) {
- case ACPI_ANY_BASE:
- case 10:
- case 16:
-
- break;
-
- default:
-
- /* Invalid Base */
-
- return_ACPI_STATUS(AE_BAD_PARAMETER);
- }
-
- if (!string) {
- goto error_exit;
- }
-
- /* Skip over any white space in the buffer */
-
- while ((*string) && (isspace((int)*string) || *string == '\t')) {
- string++;
- }
-
- if (base == ACPI_ANY_BASE) {
- /*
- * Base equal to ACPI_ANY_BASE means 'Either decimal or hex'.
- * We need to determine if it is decimal or hexadecimal.
- */
- if ((*string == '0') && (tolower((int)*(string + 1)) == 'x')) {
- sign_of0x = 1;
- base = 16;
-
- /* Skip over the leading '0x' */
- string += 2;
- } else {
- base = 10;
- }
- }
-
- /* Any string left? Check that '0x' is not followed by white space. */
-
- if (!(*string) || isspace((int)*string) || *string == '\t') {
- if (base == ACPI_ANY_BASE) {
- goto error_exit;
- } else {
- goto all_done;
- }
- }
-
- /*
- * Perform a 32-bit or 64-bit conversion, depending upon the input
- * byte width
- */
- dividend = (max_integer_byte_width <= ACPI_MAX32_BYTE_WIDTH) ?
- ACPI_UINT32_MAX : ACPI_UINT64_MAX;
-
- /* Main loop: convert the string to a 32- or 64-bit integer */
-
- while (*string) {
- if (isdigit((int)*string)) {
-
- /* Convert ASCII 0-9 to Decimal value */
-
- this_digit = ((u8)*string) - '0';
- } else if (base == 10) {
-
- /* Digit is out of range; possible in to_integer case only */
-
- term = 1;
- } else {
- this_digit = (u8)toupper((int)*string);
- if (isxdigit((int)this_digit)) {
-
- /* Convert ASCII Hex char to value */
-
- this_digit = this_digit - 'A' + 10;
- } else {
- term = 1;
- }
- }
-
- if (term) {
- if (base == ACPI_ANY_BASE) {
- goto error_exit;
- } else {
- break;
- }
- } else if ((valid_digits == 0) && (this_digit == 0)
- && !sign_of0x) {
-
- /* Skip zeros */
- string++;
- continue;
- }
-
- valid_digits++;
-
- if (sign_of0x && ((valid_digits > 16) ||
- ((valid_digits > 8)
- && (max_integer_byte_width <=
- ACPI_MAX32_BYTE_WIDTH)))) {
- /*
- * This is to_integer operation case.
- * No restrictions for string-to-integer conversion,
- * see ACPI spec.
- */
- goto error_exit;
- }
-
- /* Divide the digit into the correct position */
-
- (void)acpi_ut_short_divide((dividend - (u64)this_digit), base,
- &quotient, NULL);
-
- if (return_value > quotient) {
- if (base == ACPI_ANY_BASE) {
- goto error_exit;
- } else {
- break;
- }
- }
-
- return_value *= base;
- return_value += this_digit;
- string++;
- }
-
- /* All done, normal exit */
-
-all_done:
-
- ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n",
- ACPI_FORMAT_UINT64(return_value)));
-
- *ret_integer = return_value;
- return_ACPI_STATUS(AE_OK);
-
-error_exit:
-
- /* Base was set/validated above (10 or 16) */
-
- if (base == 10) {
- return_ACPI_STATUS(AE_BAD_DECIMAL_CONSTANT);
- } else {
- return_ACPI_STATUS(AE_BAD_HEX_CONSTANT);
- }
-}
-
-#ifdef _OBSOLETE_FUNCTIONS
-/* Removed: 01/2016 */
-
-/*******************************************************************************
- *
- * FUNCTION: strtoul64
- *
- * PARAMETERS: string - Null terminated string
- * terminater - Where a pointer to the terminating byte
- * is returned
- * base - Radix of the string
- *
- * RETURN: Converted value
- *
- * DESCRIPTION: Convert a string into an unsigned value.
- *
- ******************************************************************************/
-
-acpi_status strtoul64(char *string, u32 base, u64 *ret_integer)
-{
- u32 index;
- u32 sign;
- u64 return_value = 0;
- acpi_status status = AE_OK;
-
- *ret_integer = 0;
-
- switch (base) {
- case 0:
- case 8:
- case 10:
- case 16:
-
- break;
-
- default:
- /*
- * The specified Base parameter is not in the domain of
- * this function:
- */
- return (AE_BAD_PARAMETER);
- }
-
- /* Skip over any white space in the buffer: */
-
- while (isspace((int)*string) || *string == '\t') {
- ++string;
- }
-
- /*
- * The buffer may contain an optional plus or minus sign.
- * If it does, then skip over it but remember what is was:
- */
- if (*string == '-') {
- sign = ACPI_SIGN_NEGATIVE;
- ++string;
- } else if (*string == '+') {
- ++string;
- sign = ACPI_SIGN_POSITIVE;
- } else {
- sign = ACPI_SIGN_POSITIVE;
- }
-
- /*
- * If the input parameter Base is zero, then we need to
- * determine if it is octal, decimal, or hexadecimal:
- */
- if (base == 0) {
- if (*string == '0') {
- if (tolower((int)*(++string)) == 'x') {
- base = 16;
- ++string;
- } else {
- base = 8;
- }
- } else {
- base = 10;
- }
- }
-
- /*
- * For octal and hexadecimal bases, skip over the leading
- * 0 or 0x, if they are present.
- */
- if (base == 8 && *string == '0') {
- string++;
- }
-
- if (base == 16 && *string == '0' && tolower((int)*(++string)) == 'x') {
- string++;
- }
-
- /* Main loop: convert the string to an unsigned long */
-
- while (*string) {
- if (isdigit((int)*string)) {
- index = ((u8)*string) - '0';
- } else {
- index = (u8)toupper((int)*string);
- if (isupper((int)index)) {
- index = index - 'A' + 10;
- } else {
- goto error_exit;
- }
- }
-
- if (index >= base) {
- goto error_exit;
- }
-
- /* Check to see if value is out of range: */
-
- if (return_value > ((ACPI_UINT64_MAX - (u64)index) / (u64)base)) {
- goto error_exit;
- } else {
- return_value *= base;
- return_value += index;
- }
-
- ++string;
- }
-
- /* If a minus sign was present, then "the conversion is negated": */
-
- if (sign == ACPI_SIGN_NEGATIVE) {
- return_value = (ACPI_UINT32_MAX - return_value) + 1;
- }
-
- *ret_integer = return_value;
- return (status);
-
-error_exit:
- switch (base) {
- case 8:
-
- status = AE_BAD_OCTAL_CONSTANT;
- break;
-
- case 10:
-
- status = AE_BAD_DECIMAL_CONSTANT;
- break;
-
- case 16:
-
- status = AE_BAD_HEX_CONSTANT;
- break;
-
- default:
-
- /* Base validated above */
-
- break;
- }
-
- return (status);
-}
-#endif
diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c
index 3f5fed6..f0484b0 100644
--- a/drivers/acpi/acpica/utosi.c
+++ b/drivers/acpi/acpica/utosi.c
@@ -390,11 +390,22 @@ struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name)
* PARAMETERS: walk_state - Current walk state
*
* RETURN: Status
+ * Integer: TRUE (0) if input string is matched
+ * FALSE (-1) if string is not matched
*
* DESCRIPTION: Implementation of the _OSI predefined control method. When
* an invocation of _OSI is encountered in the system AML,
* control is transferred to this function.
*
+ * (August 2016)
+ * Note: _OSI is now defined to return "Ones" to indicate a match, for
+ * compatibility with other ACPI implementations. On a 32-bit DSDT, Ones
+ * is 0xFFFFFFFF. On a 64-bit DSDT, Ones is 0xFFFFFFFFFFFFFFFF
+ * (ACPI_UINT64_MAX).
+ *
+ * This function always returns ACPI_UINT64_MAX for TRUE, and later code
+ * will truncate this to 32 bits if necessary.
+ *
******************************************************************************/
acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
@@ -404,7 +415,7 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
struct acpi_interface_info *interface_info;
acpi_interface_handler interface_handler;
acpi_status status;
- u32 return_value;
+ u64 return_value;
ACPI_FUNCTION_TRACE(ut_osi_implementation);
@@ -444,7 +455,7 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
acpi_gbl_osi_data = interface_info->value;
}
- return_value = ACPI_UINT32_MAX;
+ return_value = ACPI_UINT64_MAX;
}
acpi_os_release_mutex(acpi_gbl_osi_mutex);
@@ -456,9 +467,10 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
*/
interface_handler = acpi_gbl_interface_handler;
if (interface_handler) {
- return_value =
- interface_handler(string_desc->string.pointer,
- return_value);
+ if (interface_handler
+ (string_desc->string.pointer, (u32)return_value)) {
+ return_value = ACPI_UINT64_MAX;
+ }
}
ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,
diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c
index 770a177..ce18346 100644
--- a/drivers/acpi/acpica/utpredef.c
+++ b/drivers/acpi/acpica/utpredef.c
@@ -176,8 +176,6 @@ void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes)
******************************************************************************/
#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP)
-#include <stdio.h>
-#include <string.h>
/* Local prototypes */
diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c
index dd084cf..40eba80 100644
--- a/drivers/acpi/acpica/utprint.c
+++ b/drivers/acpi/acpica/utprint.c
@@ -336,7 +336,7 @@ static char *acpi_ut_format_number(char *string,
/*******************************************************************************
*
- * FUNCTION: acpi_ut_vsnprintf
+ * FUNCTION: vsnprintf
*
* PARAMETERS: string - String with boundary
* size - Boundary of the string
@@ -349,9 +349,7 @@ static char *acpi_ut_format_number(char *string,
*
******************************************************************************/
-int
-acpi_ut_vsnprintf(char *string,
- acpi_size size, const char *format, va_list args)
+int vsnprintf(char *string, acpi_size size, const char *format, va_list args)
{
u8 base;
u8 type;
@@ -586,7 +584,7 @@ acpi_ut_vsnprintf(char *string,
/*******************************************************************************
*
- * FUNCTION: acpi_ut_snprintf
+ * FUNCTION: snprintf
*
* PARAMETERS: string - String with boundary
* size - Boundary of the string
@@ -598,13 +596,38 @@ acpi_ut_vsnprintf(char *string,
*
******************************************************************************/
-int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...)
+int snprintf(char *string, acpi_size size, const char *format, ...)
{
va_list args;
int length;
va_start(args, format);
- length = acpi_ut_vsnprintf(string, size, format, args);
+ length = vsnprintf(string, size, format, args);
+ va_end(args);
+
+ return (length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: sprintf
+ *
+ * PARAMETERS: string - String with boundary
+ * Format, ... - Standard printf format
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to a string.
+ *
+ ******************************************************************************/
+
+int sprintf(char *string, const char *format, ...)
+{
+ va_list args;
+ int length;
+
+ va_start(args, format);
+ length = vsnprintf(string, ACPI_UINT32_MAX, format, args);
va_end(args);
return (length);
@@ -613,7 +636,59 @@ int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...)
#ifdef ACPI_APPLICATION
/*******************************************************************************
*
- * FUNCTION: acpi_ut_file_vprintf
+ * FUNCTION: vprintf
+ *
+ * PARAMETERS: format - Standard printf format
+ * args - Argument list
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to stdout using argument list pointer.
+ *
+ ******************************************************************************/
+
+int vprintf(const char *format, va_list args)
+{
+ acpi_cpu_flags flags;
+ int length;
+
+ flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
+ length = vsnprintf(acpi_gbl_print_buffer,
+ sizeof(acpi_gbl_print_buffer), format, args);
+
+ (void)fwrite(acpi_gbl_print_buffer, length, 1, ACPI_FILE_OUT);
+ acpi_os_release_lock(acpi_gbl_print_lock, flags);
+
+ return (length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: printf
+ *
+ * PARAMETERS: Format, ... - Standard printf format
+ *
+ * RETURN: Number of bytes actually written.
+ *
+ * DESCRIPTION: Formatted output to stdout.
+ *
+ ******************************************************************************/
+
+int printf(const char *format, ...)
+{
+ va_list args;
+ int length;
+
+ va_start(args, format);
+ length = vprintf(format, args);
+ va_end(args);
+
+ return (length);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: vfprintf
*
* PARAMETERS: file - File descriptor
* format - Standard printf format
@@ -625,16 +700,16 @@ int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...)
*
******************************************************************************/
-int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args)
+int vfprintf(FILE * file, const char *format, va_list args)
{
acpi_cpu_flags flags;
int length;
flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
- length = acpi_ut_vsnprintf(acpi_gbl_print_buffer,
- sizeof(acpi_gbl_print_buffer), format, args);
+ length = vsnprintf(acpi_gbl_print_buffer,
+ sizeof(acpi_gbl_print_buffer), format, args);
- (void)acpi_os_write_file(file, acpi_gbl_print_buffer, length, 1);
+ (void)fwrite(acpi_gbl_print_buffer, length, 1, file);
acpi_os_release_lock(acpi_gbl_print_lock, flags);
return (length);
@@ -642,7 +717,7 @@ int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args)
/*******************************************************************************
*
- * FUNCTION: acpi_ut_file_printf
+ * FUNCTION: fprintf
*
* PARAMETERS: file - File descriptor
* Format, ... - Standard printf format
@@ -653,13 +728,13 @@ int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args)
*
******************************************************************************/
-int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...)
+int fprintf(FILE * file, const char *format, ...)
{
va_list args;
int length;
va_start(args, format);
- length = acpi_ut_file_vprintf(file, format, args);
+ length = vfprintf(file, format, args);
va_end(args);
return (length);
diff --git a/drivers/acpi/acpica/utstrtoul64.c b/drivers/acpi/acpica/utstrtoul64.c
new file mode 100644
index 0000000..b4f341c
--- /dev/null
+++ b/drivers/acpi/acpica/utstrtoul64.c
@@ -0,0 +1,348 @@
+/*******************************************************************************
+ *
+ * Module Name: utstrtoul64 - string to 64-bit integer support
+ *
+ ******************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2016, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+
+/*******************************************************************************
+ *
+ * The functions in this module satisfy the need for 64-bit string-to-integer
+ * conversions on both 32-bit and 64-bit platforms.
+ *
+ ******************************************************************************/
+
+#define _COMPONENT ACPI_UTILITIES
+ACPI_MODULE_NAME("utstrtoul64")
+
+/* Local prototypes */
+static u64 acpi_ut_strtoul_base10(char *string, u32 flags);
+
+static u64 acpi_ut_strtoul_base16(char *string, u32 flags);
+
+/*******************************************************************************
+ *
+ * String conversion rules as written in the ACPI specification. The error
+ * conditions and behavior are different depending on the type of conversion.
+ *
+ *
+ * Implicit data type conversion: string-to-integer
+ * --------------------------------------------------
+ *
+ * Base is always 16. This is the ACPI_STRTOUL_BASE16 case.
+ *
+ * Example:
+ * Add ("BA98", Arg0, Local0)
+ *
+ * The integer is initialized to the value zero.
+ * The ASCII string is interpreted as a hexadecimal constant.
+ *
+ * 1) A "0x" prefix is not allowed. However, ACPICA allows this for
+ * compatibility with previous ACPICA. (NO ERROR)
+ *
+ * 2) Terminates when the size of an integer is reached (32 or 64 bits).
+ * (NO ERROR)
+ *
+ * 3) The first non-hex character terminates the conversion without error.
+ * (NO ERROR)
+ *
+ * 4) Conversion of a null (zero-length) string to an integer is not
+ * allowed. However, ACPICA allows this for compatibility with previous
+ * ACPICA. This conversion returns the value 0. (NO ERROR)
+ *
+ *
+ * Explicit data type conversion: to_integer() with string operand
+ * ---------------------------------------------------------------
+ *
+ * Base is either 10 (default) or 16 (with 0x prefix)
+ *
+ * Examples:
+ * to_integer ("1000")
+ * to_integer ("0xABCD")
+ *
+ * 1) Can be (must be) either a decimal or hexadecimal numeric string.
+ * A hex value must be prefixed by "0x" or it is interpreted as a decimal.
+ *
+ * 2) The value must not exceed the maximum of an integer value. ACPI spec
+ * states the behavior is "unpredictable", so ACPICA matches the behavior
+ * of the implicit conversion case.(NO ERROR)
+ *
+ * 3) Behavior on the first non-hex character is not specified by the ACPI
+ * spec, so ACPICA matches the behavior of the implicit conversion case
+ * and terminates. (NO ERROR)
+ *
+ * 4) A null (zero-length) string is illegal.
+ * However, ACPICA allows this for compatibility with previous ACPICA.
+ * This conversion returns the value 0. (NO ERROR)
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_strtoul64
+ *
+ * PARAMETERS: string - Null terminated input string
+ * flags - Conversion info, see below
+ * return_value - Where the converted integer is
+ * returned
+ *
+ * RETURN: Status and Converted value
+ *
+ * DESCRIPTION: Convert a string into an unsigned value. Performs either a
+ * 32-bit or 64-bit conversion, depending on the input integer
+ * size in Flags (often the current mode of the interpreter).
+ *
+ * Values for Flags:
+ * ACPI_STRTOUL_32BIT - Max integer value is 32 bits
+ * ACPI_STRTOUL_64BIT - Max integer value is 64 bits
+ * ACPI_STRTOUL_BASE16 - Input string is hexadecimal. Default
+ * is 10/16 based on string prefix (0x).
+ *
+ * NOTES:
+ * Negative numbers are not supported, as they are not supported by ACPI.
+ *
+ * Supports only base 16 or base 10 strings/values. Does not
+ * support Octal strings, as these are not supported by ACPI.
+ *
+ * Current users of this support:
+ *
+ * interpreter - Implicit and explicit conversions, GPE method names
+ * debugger - Command line input string conversion
+ * iASL - Main parser, conversion of constants to integers
+ * iASL - Data Table Compiler parser (constant math expressions)
+ * iASL - Preprocessor (constant math expressions)
+ * acpi_dump - Input table addresses
+ * acpi_exec - Testing of the acpi_ut_strtoul64 function
+ *
+ * Note concerning callers:
+ * acpi_gbl_integer_byte_width can be used to set the 32/64 limit. If used,
+ * this global should be set to the proper width. For the core ACPICA code,
+ * this width depends on the DSDT version. For iASL, the default byte
+ * width is always 8 for the parser, but error checking is performed later
+ * to flag cases where a 64-bit constant is defined in a 32-bit DSDT/SSDT.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *return_value)
+{
+ acpi_status status = AE_OK;
+ u32 base;
+
+ ACPI_FUNCTION_TRACE_STR(ut_strtoul64, string);
+
+ /* Parameter validation */
+
+ if (!string || !return_value) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ *return_value = 0;
+
+ /* Check for zero-length string, returns 0 */
+
+ if (*string == 0) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /* Skip over any white space at start of string */
+
+ while (isspace((int)*string)) {
+ string++;
+ }
+
+ /* End of string? return 0 */
+
+ if (*string == 0) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * 1) The "0x" prefix indicates base 16. Per the ACPI specification,
+ * the "0x" prefix is only allowed for implicit (non-strict) conversions.
+ * However, we always allow it for compatibility with older ACPICA.
+ */
+ if ((*string == ACPI_ASCII_ZERO) &&
+ (tolower((int)*(string + 1)) == 'x')) {
+ string += 2; /* Go past the 0x */
+ if (*string == 0) {
+ return_ACPI_STATUS(AE_OK); /* Return value 0 */
+ }
+
+ base = 16;
+ }
+
+ /* 2) Force to base 16 (implicit conversion case) */
+
+ else if (flags & ACPI_STRTOUL_BASE16) {
+ base = 16;
+ }
+
+ /* 3) Default fallback is to Base 10 */
+
+ else {
+ base = 10;
+ }
+
+ /* Skip all leading zeros */
+
+ while (*string == ACPI_ASCII_ZERO) {
+ string++;
+ if (*string == 0) {
+ return_ACPI_STATUS(AE_OK); /* Return value 0 */
+ }
+ }
+
+ /* Perform the base 16 or 10 conversion */
+
+ if (base == 16) {
+ *return_value = acpi_ut_strtoul_base16(string, flags);
+ } else {
+ *return_value = acpi_ut_strtoul_base10(string, flags);
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_strtoul_base10
+ *
+ * PARAMETERS: string - Null terminated input string
+ * flags - Conversion info
+ *
+ * RETURN: 64-bit converted integer
+ *
+ * DESCRIPTION: Performs a base 10 conversion of the input string to an
+ * integer value, either 32 or 64 bits.
+ * Note: String must be valid and non-null.
+ *
+ ******************************************************************************/
+
+static u64 acpi_ut_strtoul_base10(char *string, u32 flags)
+{
+ int ascii_digit;
+ u64 next_value;
+ u64 return_value = 0;
+
+ /* Main loop: convert each ASCII byte in the input string */
+
+ while (*string) {
+ ascii_digit = *string;
+ if (!isdigit(ascii_digit)) {
+
+ /* Not ASCII 0-9, terminate */
+
+ goto exit;
+ }
+
+ /* Convert and insert (add) the decimal digit */
+
+ next_value =
+ (return_value * 10) + (ascii_digit - ACPI_ASCII_ZERO);
+
+ /* Check for overflow (32 or 64 bit) - return current converted value */
+
+ if (((flags & ACPI_STRTOUL_32BIT) && (next_value > ACPI_UINT32_MAX)) || (next_value < return_value)) { /* 64-bit overflow case */
+ goto exit;
+ }
+
+ return_value = next_value;
+ string++;
+ }
+
+exit:
+ return (return_value);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ut_strtoul_base16
+ *
+ * PARAMETERS: string - Null terminated input string
+ * flags - conversion info
+ *
+ * RETURN: 64-bit converted integer
+ *
+ * DESCRIPTION: Performs a base 16 conversion of the input string to an
+ * integer value, either 32 or 64 bits.
+ * Note: String must be valid and non-null.
+ *
+ ******************************************************************************/
+
+static u64 acpi_ut_strtoul_base16(char *string, u32 flags)
+{
+ int ascii_digit;
+ u32 valid_digits = 1;
+ u64 return_value = 0;
+
+ /* Main loop: convert each ASCII byte in the input string */
+
+ while (*string) {
+
+ /* Check for overflow (32 or 64 bit) - return current converted value */
+
+ if ((valid_digits > 16) ||
+ ((valid_digits > 8) && (flags & ACPI_STRTOUL_32BIT))) {
+ goto exit;
+ }
+
+ ascii_digit = *string;
+ if (!isxdigit(ascii_digit)) {
+
+ /* Not Hex ASCII A-F, a-f, or 0-9, terminate */
+
+ goto exit;
+ }
+
+ /* Convert and insert the hex digit */
+
+ return_value =
+ (return_value << 4) |
+ acpi_ut_ascii_char_to_hex(ascii_digit);
+
+ string++;
+ valid_digits++;
+ }
+
+exit:
+ return (return_value);
+}
diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c
index 0df07df..df31d71 100644
--- a/drivers/acpi/acpica/uttrack.c
+++ b/drivers/acpi/acpica/uttrack.c
@@ -95,13 +95,11 @@ acpi_ut_create_list(const char *list_name,
{
struct acpi_memory_list *cache;
- cache = acpi_os_allocate(sizeof(struct acpi_memory_list));
+ cache = acpi_os_allocate_zeroed(sizeof(struct acpi_memory_list));
if (!cache) {
return (AE_NO_MEMORY);
}
- memset(cache, 0, sizeof(struct acpi_memory_list));
-
cache->list_name = list_name;
cache->object_size = object_size;
diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c
index d9e6aac..ec503c8 100644
--- a/drivers/acpi/acpica/utxface.c
+++ b/drivers/acpi/acpica/utxface.c
@@ -61,7 +61,7 @@ ACPI_MODULE_NAME("utxface")
* DESCRIPTION: Shutdown the ACPICA subsystem and release all resources.
*
******************************************************************************/
-acpi_status __init acpi_terminate(void)
+acpi_status ACPI_INIT_FUNCTION acpi_terminate(void)
{
acpi_status status;
diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c
index 75b5f27..a5ca0f5 100644
--- a/drivers/acpi/acpica/utxfinit.c
+++ b/drivers/acpi/acpica/utxfinit.c
@@ -69,7 +69,7 @@ void ae_do_object_overrides(void);
*
******************************************************************************/
-acpi_status __init acpi_initialize_subsystem(void)
+acpi_status ACPI_INIT_FUNCTION acpi_initialize_subsystem(void)
{
acpi_status status;
@@ -141,7 +141,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_subsystem)
* Puts system into ACPI mode if it isn't already.
*
******************************************************************************/
-acpi_status __init acpi_enable_subsystem(u32 flags)
+acpi_status ACPI_INIT_FUNCTION acpi_enable_subsystem(u32 flags)
{
acpi_status status = AE_OK;
@@ -239,7 +239,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_enable_subsystem)
* objects and executing AML code for Regions, buffers, etc.
*
******************************************************************************/
-acpi_status __init acpi_initialize_objects(u32 flags)
+acpi_status ACPI_INIT_FUNCTION acpi_initialize_objects(u32 flags)
{
acpi_status status = AE_OK;
@@ -265,7 +265,8 @@ acpi_status __init acpi_initialize_objects(u32 flags)
* all of the tables have been loaded. It is a legacy option and is
* not compatible with other ACPI implementations. See acpi_ns_load_table.
*/
- if (acpi_gbl_group_module_level_code) {
+ if (!acpi_gbl_parse_table_as_term_list
+ && acpi_gbl_group_module_level_code) {
acpi_ns_exec_module_code_list();
/*
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
new file mode 100644
index 0000000..4616da4
--- /dev/null
+++ b/drivers/acpi/arm64/Kconfig
@@ -0,0 +1,6 @@
+#
+# ACPI Configuration for ARM64
+#
+
+config ACPI_IORT
+ bool
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
new file mode 100644
index 0000000..72331f2
--- /dev/null
+++ b/drivers/acpi/arm64/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ACPI_IORT) += iort.o
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
new file mode 100644
index 0000000..6b81746
--- /dev/null
+++ b/drivers/acpi/arm64/iort.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2016, Semihalf
+ * Author: Tomasz Nowicki <tn@semihalf.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * This file implements early detection/parsing of I/O mapping
+ * reported to OS through firmware via I/O Remapping Table (IORT)
+ * IORT document number: ARM DEN 0049A
+ */
+
+#define pr_fmt(fmt) "ACPI: IORT: " fmt
+
+#include <linux/acpi_iort.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+struct iort_its_msi_chip {
+ struct list_head list;
+ struct fwnode_handle *fw_node;
+ u32 translation_id;
+};
+
+typedef acpi_status (*iort_find_node_callback)
+ (struct acpi_iort_node *node, void *context);
+
+/* Root pointer to the mapped IORT table */
+static struct acpi_table_header *iort_table;
+
+static LIST_HEAD(iort_msi_chip_list);
+static DEFINE_SPINLOCK(iort_msi_chip_lock);
+
+/**
+ * iort_register_domain_token() - register domain token and related ITS ID
+ * to the list from where we can get it back later on.
+ * @trans_id: ITS ID.
+ * @fw_node: Domain token.
+ *
+ * Returns: 0 on success, -ENOMEM if no memory when allocating list element
+ */
+int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
+{
+ struct iort_its_msi_chip *its_msi_chip;
+
+ its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL);
+ if (!its_msi_chip)
+ return -ENOMEM;
+
+ its_msi_chip->fw_node = fw_node;
+ its_msi_chip->translation_id = trans_id;
+
+ spin_lock(&iort_msi_chip_lock);
+ list_add(&its_msi_chip->list, &iort_msi_chip_list);
+ spin_unlock(&iort_msi_chip_lock);
+
+ return 0;
+}
+
+/**
+ * iort_deregister_domain_token() - Deregister domain token based on ITS ID
+ * @trans_id: ITS ID.
+ *
+ * Returns: none.
+ */
+void iort_deregister_domain_token(int trans_id)
+{
+ struct iort_its_msi_chip *its_msi_chip, *t;
+
+ spin_lock(&iort_msi_chip_lock);
+ list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) {
+ if (its_msi_chip->translation_id == trans_id) {
+ list_del(&its_msi_chip->list);
+ kfree(its_msi_chip);
+ break;
+ }
+ }
+ spin_unlock(&iort_msi_chip_lock);
+}
+
+/**
+ * iort_find_domain_token() - Find domain token based on given ITS ID
+ * @trans_id: ITS ID.
+ *
+ * Returns: domain token when find on the list, NULL otherwise
+ */
+struct fwnode_handle *iort_find_domain_token(int trans_id)
+{
+ struct fwnode_handle *fw_node = NULL;
+ struct iort_its_msi_chip *its_msi_chip;
+
+ spin_lock(&iort_msi_chip_lock);
+ list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
+ if (its_msi_chip->translation_id == trans_id) {
+ fw_node = its_msi_chip->fw_node;
+ break;
+ }
+ }
+ spin_unlock(&iort_msi_chip_lock);
+
+ return fw_node;
+}
+
+static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
+ iort_find_node_callback callback,
+ void *context)
+{
+ struct acpi_iort_node *iort_node, *iort_end;
+ struct acpi_table_iort *iort;
+ int i;
+
+ if (!iort_table)
+ return NULL;
+
+ /* Get the first IORT node */
+ iort = (struct acpi_table_iort *)iort_table;
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+ iort->node_offset);
+ iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+ iort_table->length);
+
+ for (i = 0; i < iort->node_count; i++) {
+ if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
+ "IORT node pointer overflows, bad table!\n"))
+ return NULL;
+
+ if (iort_node->type == type &&
+ ACPI_SUCCESS(callback(iort_node, context)))
+ return iort_node;
+
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+ iort_node->length);
+ }
+
+ return NULL;
+}
+
+static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
+ void *context)
+{
+ struct device *dev = context;
+ acpi_status status;
+
+ if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
+ struct acpi_iort_named_component *ncomp;
+
+ if (!adev) {
+ status = AE_NOT_FOUND;
+ goto out;
+ }
+
+ status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(dev, "Can't get device full path name\n");
+ goto out;
+ }
+
+ ncomp = (struct acpi_iort_named_component *)node->node_data;
+ status = !strcmp(ncomp->device_name, buf.pointer) ?
+ AE_OK : AE_NOT_FOUND;
+ acpi_os_free(buf.pointer);
+ } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+ struct acpi_iort_root_complex *pci_rc;
+ struct pci_bus *bus;
+
+ bus = to_pci_bus(dev);
+ pci_rc = (struct acpi_iort_root_complex *)node->node_data;
+
+ /*
+ * It is assumed that PCI segment numbers maps one-to-one
+ * with root complexes. Each segment number can represent only
+ * one root complex.
+ */
+ status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+ AE_OK : AE_NOT_FOUND;
+ } else {
+ status = AE_NOT_FOUND;
+ }
+out:
+ return status;
+}
+
+static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
+ u32 *rid_out)
+{
+ /* Single mapping does not care for input id */
+ if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
+ if (type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+ type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+ *rid_out = map->output_base;
+ return 0;
+ }
+
+ pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n",
+ map, type);
+ return -ENXIO;
+ }
+
+ if (rid_in < map->input_base ||
+ (rid_in >= map->input_base + map->id_count))
+ return -ENXIO;
+
+ *rid_out = map->output_base + (rid_in - map->input_base);
+ return 0;
+}
+
+static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
+ u32 rid_in, u32 *rid_out,
+ u8 type)
+{
+ u32 rid = rid_in;
+
+ /* Parse the ID mapping tree to find specified node type */
+ while (node) {
+ struct acpi_iort_id_mapping *map;
+ int i;
+
+ if (node->type == type) {
+ if (rid_out)
+ *rid_out = rid;
+ return node;
+ }
+
+ if (!node->mapping_offset || !node->mapping_count)
+ goto fail_map;
+
+ map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+ node->mapping_offset);
+
+ /* Firmware bug! */
+ if (!map->output_reference) {
+ pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
+ node, node->type);
+ goto fail_map;
+ }
+
+ /* Do the RID translation */
+ for (i = 0; i < node->mapping_count; i++, map++) {
+ if (!iort_id_map(map, node->type, rid, &rid))
+ break;
+ }
+
+ if (i == node->mapping_count)
+ goto fail_map;
+
+ node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+ map->output_reference);
+ }
+
+fail_map:
+ /* Map input RID to output RID unchanged on mapping failure*/
+ if (rid_out)
+ *rid_out = rid_in;
+
+ return NULL;
+}
+
+static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
+{
+ struct pci_bus *pbus;
+
+ if (!dev_is_pci(dev))
+ return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+
+ /* Find a PCI root bus */
+ pbus = to_pci_dev(dev)->bus;
+ while (!pci_is_root_bus(pbus))
+ pbus = pbus->parent;
+
+ return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_match_node_callback, &pbus->dev);
+}
+
+/**
+ * iort_msi_map_rid() - Map a MSI requester ID for a device
+ * @dev: The device for which the mapping is to be done.
+ * @req_id: The device requester ID.
+ *
+ * Returns: mapped MSI RID on success, input requester ID otherwise
+ */
+u32 iort_msi_map_rid(struct device *dev, u32 req_id)
+{
+ struct acpi_iort_node *node;
+ u32 dev_id;
+
+ node = iort_find_dev_node(dev);
+ if (!node)
+ return req_id;
+
+ iort_node_map_rid(node, req_id, &dev_id, ACPI_IORT_NODE_ITS_GROUP);
+ return dev_id;
+}
+
+/**
+ * iort_dev_find_its_id() - Find the ITS identifier for a device
+ * @dev: The device.
+ * @idx: Index of the ITS identifier list.
+ * @its_id: ITS identifier.
+ *
+ * Returns: 0 on success, appropriate error value otherwise
+ */
+static int iort_dev_find_its_id(struct device *dev, u32 req_id,
+ unsigned int idx, int *its_id)
+{
+ struct acpi_iort_its_group *its;
+ struct acpi_iort_node *node;
+
+ node = iort_find_dev_node(dev);
+ if (!node)
+ return -ENXIO;
+
+ node = iort_node_map_rid(node, req_id, NULL, ACPI_IORT_NODE_ITS_GROUP);
+ if (!node)
+ return -ENXIO;
+
+ /* Move to ITS specific data */
+ its = (struct acpi_iort_its_group *)node->node_data;
+ if (idx > its->its_count) {
+ dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n",
+ idx, its->its_count);
+ return -ENXIO;
+ }
+
+ *its_id = its->identifiers[idx];
+ return 0;
+}
+
+/**
+ * iort_get_device_domain() - Find MSI domain related to a device
+ * @dev: The device.
+ * @req_id: Requester ID for the device.
+ *
+ * Returns: the MSI domain for this device, NULL otherwise
+ */
+struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id)
+{
+ struct fwnode_handle *handle;
+ int its_id;
+
+ if (iort_dev_find_its_id(dev, req_id, 0, &its_id))
+ return NULL;
+
+ handle = iort_find_domain_token(its_id);
+ if (!handle)
+ return NULL;
+
+ return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
+}
+
+void __init acpi_iort_init(void)
+{
+ acpi_status status;
+
+ status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
+ const char *msg = acpi_format_exception(status);
+ pr_err("Failed to get table, %s\n", msg);
+ }
+}
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index ab23479..93ecae5 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -733,15 +733,17 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
return result;
acpi_battery_init_alarm(battery);
}
+
+ result = acpi_battery_get_state(battery);
+ if (result)
+ return result;
+ acpi_battery_quirks(battery);
+
if (!battery->bat) {
result = sysfs_add_battery(battery);
if (result)
return result;
}
- result = acpi_battery_get_state(battery);
- if (result)
- return result;
- acpi_battery_quirks(battery);
/*
* Wakeup the system if battery is critical low
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 85b7d07..56190d0 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -36,6 +36,7 @@
#ifdef CONFIG_X86
#include <asm/mpspec.h>
#endif
+#include <linux/acpi_iort.h>
#include <linux/pci.h>
#include <acpi/apei.h>
#include <linux/dmi.h>
@@ -985,7 +986,8 @@ void __init acpi_early_init(void)
goto error0;
}
- if (acpi_gbl_group_module_level_code) {
+ if (!acpi_gbl_parse_table_as_term_list &&
+ acpi_gbl_group_module_level_code) {
status = acpi_load_tables();
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX
@@ -1074,7 +1076,8 @@ static int __init acpi_bus_init(void)
status = acpi_ec_ecdt_probe();
/* Ignore result. Not having an ECDT is not fatal. */
- if (!acpi_gbl_group_module_level_code) {
+ if (acpi_gbl_parse_table_as_term_list ||
+ !acpi_gbl_group_module_level_code) {
status = acpi_load_tables();
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX
@@ -1186,6 +1189,7 @@ static int __init acpi_init(void)
}
pci_mmcfg_late_init();
+ acpi_iort_init();
acpi_scan_init();
acpi_ec_init();
acpi_debugfs_init();
@@ -1193,6 +1197,7 @@ static int __init acpi_init(void)
acpi_wakeup_device_init();
acpi_debugger_init();
acpi_setup_sb_notify_handler();
+ acpi_set_processor_mapping();
return 0;
}
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 31abb0b..e19f530 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -19,6 +19,8 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#define pr_fmt(fmt) "ACPI : button: " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -104,6 +106,8 @@ struct acpi_button {
struct input_dev *input;
char phys[32]; /* for input device */
unsigned long pushed;
+ int last_state;
+ ktime_t last_time;
bool suspended;
};
@@ -111,6 +115,10 @@ static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
static struct acpi_device *lid_device;
static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
+static unsigned long lid_report_interval __read_mostly = 500;
+module_param(lid_report_interval, ulong, 0644);
+MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
+
/* --------------------------------------------------------------------------
FS Interface (/proc)
-------------------------------------------------------------------------- */
@@ -134,10 +142,79 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state)
{
struct acpi_button *button = acpi_driver_data(device);
int ret;
+ ktime_t next_report;
+ bool do_update;
+
+ /*
+ * In lid_init_state=ignore mode, if user opens/closes lid
+ * frequently with "open" missing, and "last_time" is also updated
+ * frequently, "close" cannot be delivered to the userspace.
+ * So "last_time" is only updated after a timeout or an actual
+ * switch.
+ */
+ if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
+ button->last_state != !!state)
+ do_update = true;
+ else
+ do_update = false;
+
+ next_report = ktime_add(button->last_time,
+ ms_to_ktime(lid_report_interval));
+ if (button->last_state == !!state &&
+ ktime_after(ktime_get(), next_report)) {
+ /* Complain the buggy firmware */
+ pr_warn_once("The lid device is not compliant to SW_LID.\n");
- /* input layer checks if event is redundant */
- input_report_switch(button->input, SW_LID, !state);
- input_sync(button->input);
+ /*
+ * Send the unreliable complement switch event:
+ *
+ * On most platforms, the lid device is reliable. However
+ * there are exceptions:
+ * 1. Platforms returning initial lid state as "close" by
+ * default after booting/resuming:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=89211
+ * https://bugzilla.kernel.org/show_bug.cgi?id=106151
+ * 2. Platforms never reporting "open" events:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=106941
+ * On these buggy platforms, the usage model of the ACPI
+ * lid device actually is:
+ * 1. The initial returning value of _LID may not be
+ * reliable.
+ * 2. The open event may not be reliable.
+ * 3. The close event is reliable.
+ *
+ * But SW_LID is typed as input switch event, the input
+ * layer checks if the event is redundant. Hence if the
+ * state is not switched, the userspace cannot see this
+ * platform triggered reliable event. By inserting a
+ * complement switch event, it then is guaranteed that the
+ * platform triggered reliable one can always be seen by
+ * the userspace.
+ */
+ if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
+ do_update = true;
+ /*
+ * Do generate complement switch event for "close"
+ * as "close" is reliable and wrong "open" won't
+ * trigger unexpected behaviors.
+ * Do not generate complement switch event for
+ * "open" as "open" is not reliable and wrong
+ * "close" will trigger unexpected behaviors.
+ */
+ if (!state) {
+ input_report_switch(button->input,
+ SW_LID, state);
+ input_sync(button->input);
+ }
+ }
+ }
+ /* Send the platform triggered reliable event */
+ if (do_update) {
+ input_report_switch(button->input, SW_LID, !state);
+ input_sync(button->input);
+ button->last_state = !!state;
+ button->last_time = ktime_get();
+ }
if (state)
pm_wakeup_event(&device->dev, 0);
@@ -411,6 +488,8 @@ static int acpi_button_add(struct acpi_device *device)
strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
sprintf(class, "%s/%s",
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
+ button->last_state = !!acpi_lid_evaluate_state(device);
+ button->last_time = ktime_get();
} else {
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
error = -ENODEV;
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 2e98173..d0d0504 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -40,15 +40,48 @@
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/ktime.h>
+#include <linux/rwsem.h>
+#include <linux/wait.h>
#include <acpi/cppc_acpi.h>
-/*
- * Lock to provide mutually exclusive access to the PCC
- * channel. e.g. When the remote updates the shared region
- * with new data, the reader needs to be protected from
- * other CPUs activity on the same channel.
- */
-static DEFINE_SPINLOCK(pcc_lock);
+
+struct cppc_pcc_data {
+ struct mbox_chan *pcc_channel;
+ void __iomem *pcc_comm_addr;
+ int pcc_subspace_idx;
+ bool pcc_channel_acquired;
+ ktime_t deadline;
+ unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
+
+ bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */
+ bool platform_owns_pcc; /* Ownership of PCC subspace */
+ unsigned int pcc_write_cnt; /* Running count of PCC write commands */
+
+ /*
+ * Lock to provide controlled access to the PCC channel.
+ *
+ * For performance critical usecases(currently cppc_set_perf)
+ * We need to take read_lock and check if channel belongs to OSPM
+ * before reading or writing to PCC subspace
+ * We need to take write_lock before transferring the channel
+ * ownership to the platform via a Doorbell
+ * This allows us to batch a number of CPPC requests if they happen
+ * to originate in about the same time
+ *
+ * For non-performance critical usecases(init)
+ * Take write_lock for all purposes which gives exclusive access
+ */
+ struct rw_semaphore pcc_lock;
+
+ /* Wait queue for CPUs whose requests were batched */
+ wait_queue_head_t pcc_write_wait_q;
+};
+
+/* Structure to represent the single PCC channel */
+static struct cppc_pcc_data pcc_data = {
+ .pcc_subspace_idx = -1,
+ .platform_owns_pcc = true,
+};
/*
* The cpc_desc structure contains the ACPI register details
@@ -59,18 +92,25 @@ static DEFINE_SPINLOCK(pcc_lock);
*/
static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
-/* This layer handles all the PCC specifics for CPPC. */
-static struct mbox_chan *pcc_channel;
-static void __iomem *pcc_comm_addr;
-static u64 comm_base_addr;
-static int pcc_subspace_idx = -1;
-static bool pcc_channel_acquired;
-static ktime_t deadline;
-static unsigned int pcc_mpar, pcc_mrtt;
-
/* pcc mapped address + header size + offset within PCC subspace */
-#define GET_PCC_VADDR(offs) (pcc_comm_addr + 0x8 + (offs))
-
+#define GET_PCC_VADDR(offs) (pcc_data.pcc_comm_addr + 0x8 + (offs))
+
+/* Check if a CPC regsiter is in PCC */
+#define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \
+ (cpc)->cpc_entry.reg.space_id == \
+ ACPI_ADR_SPACE_PLATFORM_COMM)
+
+/* Evalutes to True if reg is a NULL register descriptor */
+#define IS_NULL_REG(reg) ((reg)->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && \
+ (reg)->address == 0 && \
+ (reg)->bit_width == 0 && \
+ (reg)->bit_offset == 0 && \
+ (reg)->access_width == 0)
+
+/* Evalutes to True if an optional cpc field is supported */
+#define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \
+ !!(cpc)->cpc_entry.int_value : \
+ !IS_NULL_REG(&(cpc)->cpc_entry.reg))
/*
* Arbitrary Retries in case the remote processor is slow to respond
* to PCC commands. Keeping it high enough to cover emulators where
@@ -78,11 +118,79 @@ static unsigned int pcc_mpar, pcc_mrtt;
*/
#define NUM_RETRIES 500
-static int check_pcc_chan(void)
+struct cppc_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct kobject *kobj,
+ struct attribute *attr, char *buf);
+ ssize_t (*store)(struct kobject *kobj,
+ struct attribute *attr, const char *c, ssize_t count);
+};
+
+#define define_one_cppc_ro(_name) \
+static struct cppc_attr _name = \
+__ATTR(_name, 0444, show_##_name, NULL)
+
+#define to_cpc_desc(a) container_of(a, struct cpc_desc, kobj)
+
+static ssize_t show_feedback_ctrs(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
+ struct cppc_perf_fb_ctrs fb_ctrs = {0};
+
+ cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
+
+ return scnprintf(buf, PAGE_SIZE, "ref:%llu del:%llu\n",
+ fb_ctrs.reference, fb_ctrs.delivered);
+}
+define_one_cppc_ro(feedback_ctrs);
+
+static ssize_t show_reference_perf(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
+ struct cppc_perf_fb_ctrs fb_ctrs = {0};
+
+ cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n",
+ fb_ctrs.reference_perf);
+}
+define_one_cppc_ro(reference_perf);
+
+static ssize_t show_wraparound_time(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
+ struct cppc_perf_fb_ctrs fb_ctrs = {0};
+
+ cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
+
+ return scnprintf(buf, PAGE_SIZE, "%llu\n", fb_ctrs.ctr_wrap_time);
+
+}
+define_one_cppc_ro(wraparound_time);
+
+static struct attribute *cppc_attrs[] = {
+ &feedback_ctrs.attr,
+ &reference_perf.attr,
+ &wraparound_time.attr,
+ NULL
+};
+
+static struct kobj_type cppc_ktype = {
+ .sysfs_ops = &kobj_sysfs_ops,
+ .default_attrs = cppc_attrs,
+};
+
+static int check_pcc_chan(bool chk_err_bit)
{
- int ret = -EIO;
- struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_comm_addr;
- ktime_t next_deadline = ktime_add(ktime_get(), deadline);
+ int ret = -EIO, status = 0;
+ struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_data.pcc_comm_addr;
+ ktime_t next_deadline = ktime_add(ktime_get(), pcc_data.deadline);
+
+ if (!pcc_data.platform_owns_pcc)
+ return 0;
/* Retry in case the remote processor was too slow to catch up. */
while (!ktime_after(ktime_get(), next_deadline)) {
@@ -91,8 +199,11 @@ static int check_pcc_chan(void)
* platform and should have set the command completion bit when
* PCC can be used by OSPM
*/
- if (readw_relaxed(&generic_comm_base->status) & PCC_CMD_COMPLETE) {
+ status = readw_relaxed(&generic_comm_base->status);
+ if (status & PCC_CMD_COMPLETE_MASK) {
ret = 0;
+ if (chk_err_bit && (status & PCC_ERROR_MASK))
+ ret = -EIO;
break;
}
/*
@@ -102,14 +213,23 @@ static int check_pcc_chan(void)
udelay(3);
}
+ if (likely(!ret))
+ pcc_data.platform_owns_pcc = false;
+ else
+ pr_err("PCC check channel failed. Status=%x\n", status);
+
return ret;
}
+/*
+ * This function transfers the ownership of the PCC to the platform
+ * So it must be called while holding write_lock(pcc_lock)
+ */
static int send_pcc_cmd(u16 cmd)
{
- int ret = -EIO;
+ int ret = -EIO, i;
struct acpi_pcct_shared_memory *generic_comm_base =
- (struct acpi_pcct_shared_memory *) pcc_comm_addr;
+ (struct acpi_pcct_shared_memory *) pcc_data.pcc_comm_addr;
static ktime_t last_cmd_cmpl_time, last_mpar_reset;
static int mpar_count;
unsigned int time_delta;
@@ -119,20 +239,29 @@ static int send_pcc_cmd(u16 cmd)
* the channel before writing to PCC space
*/
if (cmd == CMD_READ) {
- ret = check_pcc_chan();
+ /*
+ * If there are pending cpc_writes, then we stole the channel
+ * before write completion, so first send a WRITE command to
+ * platform
+ */
+ if (pcc_data.pending_pcc_write_cmd)
+ send_pcc_cmd(CMD_WRITE);
+
+ ret = check_pcc_chan(false);
if (ret)
- return ret;
- }
+ goto end;
+ } else /* CMD_WRITE */
+ pcc_data.pending_pcc_write_cmd = FALSE;
/*
* Handle the Minimum Request Turnaround Time(MRTT)
* "The minimum amount of time that OSPM must wait after the completion
* of a command before issuing the next command, in microseconds"
*/
- if (pcc_mrtt) {
+ if (pcc_data.pcc_mrtt) {
time_delta = ktime_us_delta(ktime_get(), last_cmd_cmpl_time);
- if (pcc_mrtt > time_delta)
- udelay(pcc_mrtt - time_delta);
+ if (pcc_data.pcc_mrtt > time_delta)
+ udelay(pcc_data.pcc_mrtt - time_delta);
}
/*
@@ -146,15 +275,16 @@ static int send_pcc_cmd(u16 cmd)
* not send the request to the platform after hitting the MPAR limit in
* any 60s window
*/
- if (pcc_mpar) {
+ if (pcc_data.pcc_mpar) {
if (mpar_count == 0) {
time_delta = ktime_ms_delta(ktime_get(), last_mpar_reset);
if (time_delta < 60 * MSEC_PER_SEC) {
pr_debug("PCC cmd not sent due to MPAR limit");
- return -EIO;
+ ret = -EIO;
+ goto end;
}
last_mpar_reset = ktime_get();
- mpar_count = pcc_mpar;
+ mpar_count = pcc_data.pcc_mpar;
}
mpar_count--;
}
@@ -165,33 +295,43 @@ static int send_pcc_cmd(u16 cmd)
/* Flip CMD COMPLETE bit */
writew_relaxed(0, &generic_comm_base->status);
+ pcc_data.platform_owns_pcc = true;
+
/* Ring doorbell */
- ret = mbox_send_message(pcc_channel, &cmd);
+ ret = mbox_send_message(pcc_data.pcc_channel, &cmd);
if (ret < 0) {
pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n",
cmd, ret);
- return ret;
+ goto end;
}
- /*
- * For READs we need to ensure the cmd completed to ensure
- * the ensuing read()s can proceed. For WRITEs we dont care
- * because the actual write()s are done before coming here
- * and the next READ or WRITE will check if the channel
- * is busy/free at the entry of this call.
- *
- * If Minimum Request Turnaround Time is non-zero, we need
- * to record the completion time of both READ and WRITE
- * command for proper handling of MRTT, so we need to check
- * for pcc_mrtt in addition to CMD_READ
- */
- if (cmd == CMD_READ || pcc_mrtt) {
- ret = check_pcc_chan();
- if (pcc_mrtt)
- last_cmd_cmpl_time = ktime_get();
+ /* wait for completion and check for PCC errro bit */
+ ret = check_pcc_chan(true);
+
+ if (pcc_data.pcc_mrtt)
+ last_cmd_cmpl_time = ktime_get();
+
+ if (pcc_data.pcc_channel->mbox->txdone_irq)
+ mbox_chan_txdone(pcc_data.pcc_channel, ret);
+ else
+ mbox_client_txdone(pcc_data.pcc_channel, ret);
+
+end:
+ if (cmd == CMD_WRITE) {
+ if (unlikely(ret)) {
+ for_each_possible_cpu(i) {
+ struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i);
+ if (!desc)
+ continue;
+
+ if (desc->write_cmd_id == pcc_data.pcc_write_cnt)
+ desc->write_cmd_status = ret;
+ }
+ }
+ pcc_data.pcc_write_cnt++;
+ wake_up_all(&pcc_data.pcc_write_wait_q);
}
- mbox_client_txdone(pcc_channel, ret);
return ret;
}
@@ -272,13 +412,13 @@ end:
*
* Return: 0 for success or negative value for err.
*/
-int acpi_get_psd_map(struct cpudata **all_cpu_data)
+int acpi_get_psd_map(struct cppc_cpudata **all_cpu_data)
{
int count_target;
int retval = 0;
unsigned int i, j;
cpumask_var_t covered_cpus;
- struct cpudata *pr, *match_pr;
+ struct cppc_cpudata *pr, *match_pr;
struct acpi_psd_package *pdomain;
struct acpi_psd_package *match_pdomain;
struct cpc_desc *cpc_ptr, *match_cpc_ptr;
@@ -394,14 +534,13 @@ EXPORT_SYMBOL_GPL(acpi_get_psd_map);
static int register_pcc_channel(int pcc_subspace_idx)
{
struct acpi_pcct_hw_reduced *cppc_ss;
- unsigned int len;
u64 usecs_lat;
if (pcc_subspace_idx >= 0) {
- pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl,
+ pcc_data.pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl,
pcc_subspace_idx);
- if (IS_ERR(pcc_channel)) {
+ if (IS_ERR(pcc_data.pcc_channel)) {
pr_err("Failed to find PCC communication channel\n");
return -ENODEV;
}
@@ -412,7 +551,7 @@ static int register_pcc_channel(int pcc_subspace_idx)
* PCC channels) and stored pointers to the
* subspace communication region in con_priv.
*/
- cppc_ss = pcc_channel->con_priv;
+ cppc_ss = (pcc_data.pcc_channel)->con_priv;
if (!cppc_ss) {
pr_err("No PCC subspace found for CPPC\n");
@@ -420,35 +559,42 @@ static int register_pcc_channel(int pcc_subspace_idx)
}
/*
- * This is the shared communication region
- * for the OS and Platform to communicate over.
- */
- comm_base_addr = cppc_ss->base_address;
- len = cppc_ss->length;
-
- /*
* cppc_ss->latency is just a Nominal value. In reality
* the remote processor could be much slower to reply.
* So add an arbitrary amount of wait on top of Nominal.
*/
usecs_lat = NUM_RETRIES * cppc_ss->latency;
- deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC);
- pcc_mrtt = cppc_ss->min_turnaround_time;
- pcc_mpar = cppc_ss->max_access_rate;
+ pcc_data.deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC);
+ pcc_data.pcc_mrtt = cppc_ss->min_turnaround_time;
+ pcc_data.pcc_mpar = cppc_ss->max_access_rate;
+ pcc_data.pcc_nominal = cppc_ss->latency;
- pcc_comm_addr = acpi_os_ioremap(comm_base_addr, len);
- if (!pcc_comm_addr) {
+ pcc_data.pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length);
+ if (!pcc_data.pcc_comm_addr) {
pr_err("Failed to ioremap PCC comm region mem\n");
return -ENOMEM;
}
/* Set flag so that we dont come here for each CPU. */
- pcc_channel_acquired = true;
+ pcc_data.pcc_channel_acquired = true;
}
return 0;
}
+/**
+ * cpc_ffh_supported() - check if FFH reading supported
+ *
+ * Check if the architecture has support for functional fixed hardware
+ * read/write capability.
+ *
+ * Return: true for supported, false for not supported
+ */
+bool __weak cpc_ffh_supported(void)
+{
+ return false;
+}
+
/*
* An example CPC table looks like the following.
*
@@ -507,6 +653,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
union acpi_object *out_obj, *cpc_obj;
struct cpc_desc *cpc_ptr;
struct cpc_reg *gas_t;
+ struct device *cpu_dev;
acpi_handle handle = pr->handle;
unsigned int num_ent, i, cpc_rev;
acpi_status status;
@@ -545,6 +692,8 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free;
}
+ cpc_ptr->num_entries = num_ent;
+
/* Second entry should be revision. */
cpc_obj = &out_obj->package.elements[1];
if (cpc_obj->type == ACPI_TYPE_INTEGER) {
@@ -579,16 +728,27 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
* so extract it only once.
*/
if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
- if (pcc_subspace_idx < 0)
- pcc_subspace_idx = gas_t->access_width;
- else if (pcc_subspace_idx != gas_t->access_width) {
+ if (pcc_data.pcc_subspace_idx < 0)
+ pcc_data.pcc_subspace_idx = gas_t->access_width;
+ else if (pcc_data.pcc_subspace_idx != gas_t->access_width) {
pr_debug("Mismatched PCC ids.\n");
goto out_free;
}
- } else if (gas_t->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
- /* Support only PCC and SYS MEM type regs */
- pr_debug("Unsupported register type: %d\n", gas_t->space_id);
- goto out_free;
+ } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ if (gas_t->address) {
+ void __iomem *addr;
+
+ addr = ioremap(gas_t->address, gas_t->bit_width/8);
+ if (!addr)
+ goto out_free;
+ cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
+ }
+ } else {
+ if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) {
+ /* Support only PCC ,SYS MEM and FFH type regs */
+ pr_debug("Unsupported register type: %d\n", gas_t->space_id);
+ goto out_free;
+ }
}
cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
@@ -607,10 +767,13 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free;
/* Register PCC channel once for all CPUs. */
- if (!pcc_channel_acquired) {
- ret = register_pcc_channel(pcc_subspace_idx);
+ if (!pcc_data.pcc_channel_acquired) {
+ ret = register_pcc_channel(pcc_data.pcc_subspace_idx);
if (ret)
goto out_free;
+
+ init_rwsem(&pcc_data.pcc_lock);
+ init_waitqueue_head(&pcc_data.pcc_write_wait_q);
}
/* Plug PSD data into this CPUs CPC descriptor. */
@@ -619,10 +782,27 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
/* Everything looks okay */
pr_debug("Parsed CPC struct for CPU: %d\n", pr->id);
+ /* Add per logical CPU nodes for reading its feedback counters. */
+ cpu_dev = get_cpu_device(pr->id);
+ if (!cpu_dev)
+ goto out_free;
+
+ ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj,
+ "acpi_cppc");
+ if (ret)
+ goto out_free;
+
kfree(output.pointer);
return 0;
out_free:
+ /* Free all the mapped sys mem areas for this CPU */
+ for (i = 2; i < cpc_ptr->num_entries; i++) {
+ void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
+
+ if (addr)
+ iounmap(addr);
+ }
kfree(cpc_ptr);
out_buf_free:
@@ -640,26 +820,82 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe);
void acpi_cppc_processor_exit(struct acpi_processor *pr)
{
struct cpc_desc *cpc_ptr;
+ unsigned int i;
+ void __iomem *addr;
+
cpc_ptr = per_cpu(cpc_desc_ptr, pr->id);
+
+ /* Free all the mapped sys mem areas for this CPU */
+ for (i = 2; i < cpc_ptr->num_entries; i++) {
+ addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
+ if (addr)
+ iounmap(addr);
+ }
+
+ kobject_put(&cpc_ptr->kobj);
kfree(cpc_ptr);
}
EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
+/**
+ * cpc_read_ffh() - Read FFH register
+ * @cpunum: cpu number to read
+ * @reg: cppc register information
+ * @val: place holder for return value
+ *
+ * Read bit_width bits from a specified address and bit_offset
+ *
+ * Return: 0 for success and error code
+ */
+int __weak cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
+{
+ return -ENOTSUPP;
+}
+
+/**
+ * cpc_write_ffh() - Write FFH register
+ * @cpunum: cpu number to write
+ * @reg: cppc register information
+ * @val: value to write
+ *
+ * Write value of bit_width bits to a specified address and bit_offset
+ *
+ * Return: 0 for success and error code
+ */
+int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
+{
+ return -ENOTSUPP;
+}
+
/*
* Since cpc_read and cpc_write are called while holding pcc_lock, it should be
* as fast as possible. We have already mapped the PCC subspace during init, so
* we can directly write to it.
*/
-static int cpc_read(struct cpc_reg *reg, u64 *val)
+static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
{
int ret_val = 0;
+ void __iomem *vaddr = 0;
+ struct cpc_reg *reg = &reg_res->cpc_entry.reg;
+
+ if (reg_res->type == ACPI_TYPE_INTEGER) {
+ *val = reg_res->cpc_entry.int_value;
+ return ret_val;
+ }
*val = 0;
- if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
- void __iomem *vaddr = GET_PCC_VADDR(reg->address);
+ if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
+ vaddr = GET_PCC_VADDR(reg->address);
+ else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ vaddr = reg_res->sys_mem_vaddr;
+ else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
+ return cpc_read_ffh(cpu, reg, val);
+ else
+ return acpi_os_read_memory((acpi_physical_address)reg->address,
+ val, reg->bit_width);
- switch (reg->bit_width) {
+ switch (reg->bit_width) {
case 8:
*val = readb_relaxed(vaddr);
break;
@@ -674,23 +910,30 @@ static int cpc_read(struct cpc_reg *reg, u64 *val)
break;
default:
pr_debug("Error: Cannot read %u bit width from PCC\n",
- reg->bit_width);
+ reg->bit_width);
ret_val = -EFAULT;
- }
- } else
- ret_val = acpi_os_read_memory((acpi_physical_address)reg->address,
- val, reg->bit_width);
+ }
+
return ret_val;
}
-static int cpc_write(struct cpc_reg *reg, u64 val)
+static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
{
int ret_val = 0;
+ void __iomem *vaddr = 0;
+ struct cpc_reg *reg = &reg_res->cpc_entry.reg;
+
+ if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
+ vaddr = GET_PCC_VADDR(reg->address);
+ else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ vaddr = reg_res->sys_mem_vaddr;
+ else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
+ return cpc_write_ffh(cpu, reg, val);
+ else
+ return acpi_os_write_memory((acpi_physical_address)reg->address,
+ val, reg->bit_width);
- if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
- void __iomem *vaddr = GET_PCC_VADDR(reg->address);
-
- switch (reg->bit_width) {
+ switch (reg->bit_width) {
case 8:
writeb_relaxed(val, vaddr);
break;
@@ -705,13 +948,11 @@ static int cpc_write(struct cpc_reg *reg, u64 val)
break;
default:
pr_debug("Error: Cannot write %u bit width to PCC\n",
- reg->bit_width);
+ reg->bit_width);
ret_val = -EFAULT;
break;
- }
- } else
- ret_val = acpi_os_write_memory((acpi_physical_address)reg->address,
- val, reg->bit_width);
+ }
+
return ret_val;
}
@@ -727,8 +968,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf,
*nom_perf;
- u64 high, low, ref, nom;
- int ret = 0;
+ u64 high, low, nom;
+ int ret = 0, regs_in_pcc = 0;
if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
@@ -740,13 +981,11 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
ref_perf = &cpc_desc->cpc_regs[REFERENCE_PERF];
nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF];
- spin_lock(&pcc_lock);
-
/* Are any of the regs PCC ?*/
- if ((highest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
- (lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
- (ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
- (nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
+ if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
+ CPC_IN_PCC(ref_perf) || CPC_IN_PCC(nom_perf)) {
+ regs_in_pcc = 1;
+ down_write(&pcc_data.pcc_lock);
/* Ring doorbell once to update PCC subspace */
if (send_pcc_cmd(CMD_READ) < 0) {
ret = -EIO;
@@ -754,26 +993,21 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
}
}
- cpc_read(&highest_reg->cpc_entry.reg, &high);
+ cpc_read(cpunum, highest_reg, &high);
perf_caps->highest_perf = high;
- cpc_read(&lowest_reg->cpc_entry.reg, &low);
+ cpc_read(cpunum, lowest_reg, &low);
perf_caps->lowest_perf = low;
- cpc_read(&ref_perf->cpc_entry.reg, &ref);
- perf_caps->reference_perf = ref;
-
- cpc_read(&nom_perf->cpc_entry.reg, &nom);
+ cpc_read(cpunum, nom_perf, &nom);
perf_caps->nominal_perf = nom;
- if (!ref)
- perf_caps->reference_perf = perf_caps->nominal_perf;
-
if (!high || !low || !nom)
ret = -EFAULT;
out_err:
- spin_unlock(&pcc_lock);
+ if (regs_in_pcc)
+ up_write(&pcc_data.pcc_lock);
return ret;
}
EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
@@ -788,9 +1022,10 @@ EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
{
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
- struct cpc_register_resource *delivered_reg, *reference_reg;
- u64 delivered, reference;
- int ret = 0;
+ struct cpc_register_resource *delivered_reg, *reference_reg,
+ *ref_perf_reg, *ctr_wrap_reg;
+ u64 delivered, reference, ref_perf, ctr_wrap_time;
+ int ret = 0, regs_in_pcc = 0;
if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
@@ -799,12 +1034,21 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR];
reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR];
+ ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
+ ctr_wrap_reg = &cpc_desc->cpc_regs[CTR_WRAP_TIME];
- spin_lock(&pcc_lock);
+ /*
+ * If refernce perf register is not supported then we should
+ * use the nominal perf value
+ */
+ if (!CPC_SUPPORTED(ref_perf_reg))
+ ref_perf_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
/* Are any of the regs PCC ?*/
- if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) ||
- (reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) {
+ if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) ||
+ CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) {
+ down_write(&pcc_data.pcc_lock);
+ regs_in_pcc = 1;
/* Ring doorbell once to update PCC subspace */
if (send_pcc_cmd(CMD_READ) < 0) {
ret = -EIO;
@@ -812,25 +1056,31 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
}
}
- cpc_read(&delivered_reg->cpc_entry.reg, &delivered);
- cpc_read(&reference_reg->cpc_entry.reg, &reference);
+ cpc_read(cpunum, delivered_reg, &delivered);
+ cpc_read(cpunum, reference_reg, &reference);
+ cpc_read(cpunum, ref_perf_reg, &ref_perf);
- if (!delivered || !reference) {
+ /*
+ * Per spec, if ctr_wrap_time optional register is unsupported, then the
+ * performance counters are assumed to never wrap during the lifetime of
+ * platform
+ */
+ ctr_wrap_time = (u64)(~((u64)0));
+ if (CPC_SUPPORTED(ctr_wrap_reg))
+ cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time);
+
+ if (!delivered || !reference || !ref_perf) {
ret = -EFAULT;
goto out_err;
}
perf_fb_ctrs->delivered = delivered;
perf_fb_ctrs->reference = reference;
-
- perf_fb_ctrs->delivered -= perf_fb_ctrs->prev_delivered;
- perf_fb_ctrs->reference -= perf_fb_ctrs->prev_reference;
-
- perf_fb_ctrs->prev_delivered = delivered;
- perf_fb_ctrs->prev_reference = reference;
-
+ perf_fb_ctrs->reference_perf = ref_perf;
+ perf_fb_ctrs->ctr_wrap_time = ctr_wrap_time;
out_err:
- spin_unlock(&pcc_lock);
+ if (regs_in_pcc)
+ up_write(&pcc_data.pcc_lock);
return ret;
}
EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
@@ -855,30 +1105,142 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
- spin_lock(&pcc_lock);
-
- /* If this is PCC reg, check if channel is free before writing */
- if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
- ret = check_pcc_chan();
- if (ret)
- goto busy_channel;
+ /*
+ * This is Phase-I where we want to write to CPC registers
+ * -> We want all CPUs to be able to execute this phase in parallel
+ *
+ * Since read_lock can be acquired by multiple CPUs simultaneously we
+ * achieve that goal here
+ */
+ if (CPC_IN_PCC(desired_reg)) {
+ down_read(&pcc_data.pcc_lock); /* BEGIN Phase-I */
+ if (pcc_data.platform_owns_pcc) {
+ ret = check_pcc_chan(false);
+ if (ret) {
+ up_read(&pcc_data.pcc_lock);
+ return ret;
+ }
+ }
+ /*
+ * Update the pending_write to make sure a PCC CMD_READ will not
+ * arrive and steal the channel during the switch to write lock
+ */
+ pcc_data.pending_pcc_write_cmd = true;
+ cpc_desc->write_cmd_id = pcc_data.pcc_write_cnt;
+ cpc_desc->write_cmd_status = 0;
}
/*
* Skip writing MIN/MAX until Linux knows how to come up with
* useful values.
*/
- cpc_write(&desired_reg->cpc_entry.reg, perf_ctrls->desired_perf);
+ cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
- /* Is this a PCC reg ?*/
- if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
- /* Ring doorbell so Remote can get our perf request. */
- if (send_pcc_cmd(CMD_WRITE) < 0)
- ret = -EIO;
+ if (CPC_IN_PCC(desired_reg))
+ up_read(&pcc_data.pcc_lock); /* END Phase-I */
+ /*
+ * This is Phase-II where we transfer the ownership of PCC to Platform
+ *
+ * Short Summary: Basically if we think of a group of cppc_set_perf
+ * requests that happened in short overlapping interval. The last CPU to
+ * come out of Phase-I will enter Phase-II and ring the doorbell.
+ *
+ * We have the following requirements for Phase-II:
+ * 1. We want to execute Phase-II only when there are no CPUs
+ * currently executing in Phase-I
+ * 2. Once we start Phase-II we want to avoid all other CPUs from
+ * entering Phase-I.
+ * 3. We want only one CPU among all those who went through Phase-I
+ * to run phase-II
+ *
+ * If write_trylock fails to get the lock and doesn't transfer the
+ * PCC ownership to the platform, then one of the following will be TRUE
+ * 1. There is at-least one CPU in Phase-I which will later execute
+ * write_trylock, so the CPUs in Phase-I will be responsible for
+ * executing the Phase-II.
+ * 2. Some other CPU has beaten this CPU to successfully execute the
+ * write_trylock and has already acquired the write_lock. We know for a
+ * fact it(other CPU acquiring the write_lock) couldn't have happened
+ * before this CPU's Phase-I as we held the read_lock.
+ * 3. Some other CPU executing pcc CMD_READ has stolen the
+ * down_write, in which case, send_pcc_cmd will check for pending
+ * CMD_WRITE commands by checking the pending_pcc_write_cmd.
+ * So this CPU can be certain that its request will be delivered
+ * So in all cases, this CPU knows that its request will be delivered
+ * by another CPU and can return
+ *
+ * After getting the down_write we still need to check for
+ * pending_pcc_write_cmd to take care of the following scenario
+ * The thread running this code could be scheduled out between
+ * Phase-I and Phase-II. Before it is scheduled back on, another CPU
+ * could have delivered the request to Platform by triggering the
+ * doorbell and transferred the ownership of PCC to platform. So this
+ * avoids triggering an unnecessary doorbell and more importantly before
+ * triggering the doorbell it makes sure that the PCC channel ownership
+ * is still with OSPM.
+ * pending_pcc_write_cmd can also be cleared by a different CPU, if
+ * there was a pcc CMD_READ waiting on down_write and it steals the lock
+ * before the pcc CMD_WRITE is completed. pcc_send_cmd checks for this
+ * case during a CMD_READ and if there are pending writes it delivers
+ * the write command before servicing the read command
+ */
+ if (CPC_IN_PCC(desired_reg)) {
+ if (down_write_trylock(&pcc_data.pcc_lock)) { /* BEGIN Phase-II */
+ /* Update only if there are pending write commands */
+ if (pcc_data.pending_pcc_write_cmd)
+ send_pcc_cmd(CMD_WRITE);
+ up_write(&pcc_data.pcc_lock); /* END Phase-II */
+ } else
+ /* Wait until pcc_write_cnt is updated by send_pcc_cmd */
+ wait_event(pcc_data.pcc_write_wait_q,
+ cpc_desc->write_cmd_id != pcc_data.pcc_write_cnt);
+
+ /* send_pcc_cmd updates the status in case of failure */
+ ret = cpc_desc->write_cmd_status;
}
-busy_channel:
- spin_unlock(&pcc_lock);
-
return ret;
}
EXPORT_SYMBOL_GPL(cppc_set_perf);
+
+/**
+ * cppc_get_transition_latency - returns frequency transition latency in ns
+ *
+ * ACPI CPPC does not explicitly specifiy how a platform can specify the
+ * transition latency for perfromance change requests. The closest we have
+ * is the timing information from the PCCT tables which provides the info
+ * on the number and frequency of PCC commands the platform can handle.
+ */
+unsigned int cppc_get_transition_latency(int cpu_num)
+{
+ /*
+ * Expected transition latency is based on the PCCT timing values
+ * Below are definition from ACPI spec:
+ * pcc_nominal- Expected latency to process a command, in microseconds
+ * pcc_mpar - The maximum number of periodic requests that the subspace
+ * channel can support, reported in commands per minute. 0
+ * indicates no limitation.
+ * pcc_mrtt - The minimum amount of time that OSPM must wait after the
+ * completion of a command before issuing the next command,
+ * in microseconds.
+ */
+ unsigned int latency_ns = 0;
+ struct cpc_desc *cpc_desc;
+ struct cpc_register_resource *desired_reg;
+
+ cpc_desc = per_cpu(cpc_desc_ptr, cpu_num);
+ if (!cpc_desc)
+ return CPUFREQ_ETERNAL;
+
+ desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
+ if (!CPC_IN_PCC(desired_reg))
+ return CPUFREQ_ETERNAL;
+
+ if (pcc_data.pcc_mpar)
+ latency_ns = 60 * (1000 * 1000 * 1000 / pcc_data.pcc_mpar);
+
+ latency_ns = max(latency_ns, pcc_data.pcc_nominal * 1000);
+ latency_ns = max(latency_ns, pcc_data.pcc_mrtt * 1000);
+
+ return latency_ns;
+}
+EXPORT_SYMBOL_GPL(cppc_get_transition_latency);
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index e7bd57c..6805310 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -104,10 +104,12 @@ enum ec_command {
#define ACPI_EC_MAX_QUERIES 16 /* Maximum number of parallel queries */
enum {
+ EC_FLAGS_QUERY_ENABLED, /* Query is enabled */
EC_FLAGS_QUERY_PENDING, /* Query is pending */
EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */
EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */
EC_FLAGS_EC_HANDLER_INSTALLED, /* OpReg handler installed */
+ EC_FLAGS_EVT_HANDLER_INSTALLED, /* _Qxx handlers installed */
EC_FLAGS_STARTED, /* Driver is started */
EC_FLAGS_STOPPED, /* Driver is stopped */
EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the
@@ -145,6 +147,10 @@ static unsigned int ec_storm_threshold __read_mostly = 8;
module_param(ec_storm_threshold, uint, 0644);
MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm");
+static bool ec_freeze_events __read_mostly = true;
+module_param(ec_freeze_events, bool, 0644);
+MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume");
+
struct acpi_ec_query_handler {
struct list_head node;
acpi_ec_query_func func;
@@ -179,6 +185,7 @@ static void acpi_ec_event_processor(struct work_struct *work);
struct acpi_ec *boot_ec, *first_ec;
EXPORT_SYMBOL(first_ec);
+static bool boot_ec_is_ecdt = false;
static struct workqueue_struct *ec_query_wq;
static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
@@ -239,6 +246,30 @@ static bool acpi_ec_started(struct acpi_ec *ec)
!test_bit(EC_FLAGS_STOPPED, &ec->flags);
}
+static bool acpi_ec_event_enabled(struct acpi_ec *ec)
+{
+ /*
+ * There is an OSPM early stage logic. During the early stages
+ * (boot/resume), OSPMs shouldn't enable the event handling, only
+ * the EC transactions are allowed to be performed.
+ */
+ if (!test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
+ return false;
+ /*
+ * However, disabling the event handling is experimental for late
+ * stage (suspend), and is controlled by the boot parameter of
+ * "ec_freeze_events":
+ * 1. true: The EC event handling is disabled before entering
+ * the noirq stage.
+ * 2. false: The EC event handling is automatically disabled as
+ * soon as the EC driver is stopped.
+ */
+ if (ec_freeze_events)
+ return acpi_ec_started(ec);
+ else
+ return test_bit(EC_FLAGS_STARTED, &ec->flags);
+}
+
static bool acpi_ec_flushed(struct acpi_ec *ec)
{
return ec->reference_count == 1;
@@ -429,7 +460,8 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
static void acpi_ec_submit_query(struct acpi_ec *ec)
{
- if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
+ if (acpi_ec_event_enabled(ec) &&
+ !test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
ec_dbg_evt("Command(%s) submitted/blocked",
acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
ec->nr_pending_queries++;
@@ -446,6 +478,86 @@ static void acpi_ec_complete_query(struct acpi_ec *ec)
}
}
+static inline void __acpi_ec_enable_event(struct acpi_ec *ec)
+{
+ if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
+ ec_log_drv("event unblocked");
+ if (!test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
+ advance_transaction(ec);
+}
+
+static inline void __acpi_ec_disable_event(struct acpi_ec *ec)
+{
+ if (test_and_clear_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
+ ec_log_drv("event blocked");
+}
+
+/*
+ * Process _Q events that might have accumulated in the EC.
+ * Run with locked ec mutex.
+ */
+static void acpi_ec_clear(struct acpi_ec *ec)
+{
+ int i, status;
+ u8 value = 0;
+
+ for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
+ status = acpi_ec_query(ec, &value);
+ if (status || !value)
+ break;
+ }
+ if (unlikely(i == ACPI_EC_CLEAR_MAX))
+ pr_warn("Warning: Maximum of %d stale EC events cleared\n", i);
+ else
+ pr_info("%d stale EC events cleared\n", i);
+}
+
+static void acpi_ec_enable_event(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ if (acpi_ec_started(ec))
+ __acpi_ec_enable_event(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+
+ /* Drain additional events if hardware requires that */
+ if (EC_FLAGS_CLEAR_ON_RESUME)
+ acpi_ec_clear(ec);
+}
+
+static bool acpi_ec_query_flushed(struct acpi_ec *ec)
+{
+ bool flushed;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ flushed = !ec->nr_pending_queries;
+ spin_unlock_irqrestore(&ec->lock, flags);
+ return flushed;
+}
+
+static void __acpi_ec_flush_event(struct acpi_ec *ec)
+{
+ /*
+ * When ec_freeze_events is true, we need to flush events in
+ * the proper position before entering the noirq stage.
+ */
+ wait_event(ec->wait, acpi_ec_query_flushed(ec));
+ if (ec_query_wq)
+ flush_workqueue(ec_query_wq);
+}
+
+static void acpi_ec_disable_event(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ec->lock, flags);
+ __acpi_ec_disable_event(ec);
+ spin_unlock_irqrestore(&ec->lock, flags);
+ __acpi_ec_flush_event(ec);
+}
+
static bool acpi_ec_guard_event(struct acpi_ec *ec)
{
bool guarded = true;
@@ -832,27 +944,6 @@ acpi_handle ec_get_handle(void)
}
EXPORT_SYMBOL(ec_get_handle);
-/*
- * Process _Q events that might have accumulated in the EC.
- * Run with locked ec mutex.
- */
-static void acpi_ec_clear(struct acpi_ec *ec)
-{
- int i, status;
- u8 value = 0;
-
- for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
- status = acpi_ec_query(ec, &value);
- if (status || !value)
- break;
- }
-
- if (unlikely(i == ACPI_EC_CLEAR_MAX))
- pr_warn("Warning: Maximum of %d stale EC events cleared\n", i);
- else
- pr_info("%d stale EC events cleared\n", i);
-}
-
static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
{
unsigned long flags;
@@ -896,7 +987,8 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
if (!suspending) {
acpi_ec_complete_request(ec);
ec_dbg_ref(ec, "Decrease driver");
- }
+ } else if (!ec_freeze_events)
+ __acpi_ec_disable_event(ec);
clear_bit(EC_FLAGS_STARTED, &ec->flags);
clear_bit(EC_FLAGS_STOPPED, &ec->flags);
ec_log_drv("EC stopped");
@@ -919,20 +1011,6 @@ void acpi_ec_block_transactions(void)
void acpi_ec_unblock_transactions(void)
{
- struct acpi_ec *ec = first_ec;
-
- if (!ec)
- return;
-
- /* Allow transactions to be carried out again */
- acpi_ec_start(ec, true);
-
- if (EC_FLAGS_CLEAR_ON_RESUME)
- acpi_ec_clear(ec);
-}
-
-void acpi_ec_unblock_transactions_early(void)
-{
/*
* Allow transactions to happen again (this function is called from
* atomic context during wakeup, so we don't need to acquire the mutex).
@@ -1228,13 +1306,21 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
static acpi_status
ec_parse_io_ports(struct acpi_resource *resource, void *context);
-static struct acpi_ec *make_acpi_ec(void)
+static void acpi_ec_free(struct acpi_ec *ec)
+{
+ if (first_ec == ec)
+ first_ec = NULL;
+ if (boot_ec == ec)
+ boot_ec = NULL;
+ kfree(ec);
+}
+
+static struct acpi_ec *acpi_ec_alloc(void)
{
struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
if (!ec)
return NULL;
- ec->flags = 1 << EC_FLAGS_QUERY_PENDING;
mutex_init(&ec->mutex);
init_waitqueue_head(&ec->wait);
INIT_LIST_HEAD(&ec->list);
@@ -1290,7 +1376,12 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
return AE_CTRL_TERMINATE;
}
-static int ec_install_handlers(struct acpi_ec *ec)
+/*
+ * Note: This function returns an error code only when the address space
+ * handler is not installed, which means "not able to handle
+ * transactions".
+ */
+static int ec_install_handlers(struct acpi_ec *ec, bool handle_events)
{
acpi_status status;
@@ -1319,6 +1410,16 @@ static int ec_install_handlers(struct acpi_ec *ec)
set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
}
+ if (!handle_events)
+ return 0;
+
+ if (!test_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags)) {
+ /* Find and register all query methods */
+ acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
+ acpi_ec_register_query_methods,
+ NULL, ec, NULL);
+ set_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags);
+ }
if (!test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {
status = acpi_install_gpe_raw_handler(NULL, ec->gpe,
ACPI_GPE_EDGE_TRIGGERED,
@@ -1329,6 +1430,9 @@ static int ec_install_handlers(struct acpi_ec *ec)
if (test_bit(EC_FLAGS_STARTED, &ec->flags) &&
ec->reference_count >= 1)
acpi_ec_enable_gpe(ec, true);
+
+ /* EC is fully operational, allow queries */
+ acpi_ec_enable_event(ec);
}
}
@@ -1363,23 +1467,104 @@ static void ec_remove_handlers(struct acpi_ec *ec)
pr_err("failed to remove gpe handler\n");
clear_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags);
}
+ if (test_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags)) {
+ acpi_ec_remove_query_handlers(ec, true, 0);
+ clear_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags);
+ }
}
-static struct acpi_ec *acpi_ec_alloc(void)
+static int acpi_ec_setup(struct acpi_ec *ec, bool handle_events)
{
- struct acpi_ec *ec;
+ int ret;
- /* Check for boot EC */
- if (boot_ec) {
- ec = boot_ec;
- boot_ec = NULL;
- ec_remove_handlers(ec);
- if (first_ec == ec)
- first_ec = NULL;
- } else {
- ec = make_acpi_ec();
+ ret = ec_install_handlers(ec, handle_events);
+ if (ret)
+ return ret;
+
+ /* First EC capable of handling transactions */
+ if (!first_ec) {
+ first_ec = ec;
+ acpi_handle_info(first_ec->handle, "Used as first EC\n");
}
- return ec;
+
+ acpi_handle_info(ec->handle,
+ "GPE=0x%lx, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n",
+ ec->gpe, ec->command_addr, ec->data_addr);
+ return ret;
+}
+
+static int acpi_config_boot_ec(struct acpi_ec *ec, acpi_handle handle,
+ bool handle_events, bool is_ecdt)
+{
+ int ret;
+
+ /*
+ * Changing the ACPI handle results in a re-configuration of the
+ * boot EC. And if it happens after the namespace initialization,
+ * it causes _REG evaluations.
+ */
+ if (boot_ec && boot_ec->handle != handle)
+ ec_remove_handlers(boot_ec);
+
+ /* Unset old boot EC */
+ if (boot_ec != ec)
+ acpi_ec_free(boot_ec);
+
+ /*
+ * ECDT device creation is split into acpi_ec_ecdt_probe() and
+ * acpi_ec_ecdt_start(). This function takes care of completing the
+ * ECDT parsing logic as the handle update should be performed
+ * between the installation/uninstallation of the handlers.
+ */
+ if (ec->handle != handle)
+ ec->handle = handle;
+
+ ret = acpi_ec_setup(ec, handle_events);
+ if (ret)
+ return ret;
+
+ /* Set new boot EC */
+ if (!boot_ec) {
+ boot_ec = ec;
+ boot_ec_is_ecdt = is_ecdt;
+ }
+
+ acpi_handle_info(boot_ec->handle,
+ "Used as boot %s EC to handle transactions%s\n",
+ is_ecdt ? "ECDT" : "DSDT",
+ handle_events ? " and events" : "");
+ return ret;
+}
+
+static bool acpi_ec_ecdt_get_handle(acpi_handle *phandle)
+{
+ struct acpi_table_ecdt *ecdt_ptr;
+ acpi_status status;
+ acpi_handle handle;
+
+ status = acpi_get_table(ACPI_SIG_ECDT, 1,
+ (struct acpi_table_header **)&ecdt_ptr);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ status = acpi_get_handle(NULL, ecdt_ptr->id, &handle);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ *phandle = handle;
+ return true;
+}
+
+static bool acpi_is_boot_ec(struct acpi_ec *ec)
+{
+ if (!boot_ec)
+ return false;
+ if (ec->handle == boot_ec->handle &&
+ ec->gpe == boot_ec->gpe &&
+ ec->command_addr == boot_ec->command_addr &&
+ ec->data_addr == boot_ec->data_addr)
+ return true;
+ return false;
}
static int acpi_ec_add(struct acpi_device *device)
@@ -1395,16 +1580,21 @@ static int acpi_ec_add(struct acpi_device *device)
return -ENOMEM;
if (ec_parse_device(device->handle, 0, ec, NULL) !=
AE_CTRL_TERMINATE) {
- kfree(ec);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_alloc;
}
- /* Find and register all query methods */
- acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
- acpi_ec_register_query_methods, NULL, ec, NULL);
+ if (acpi_is_boot_ec(ec)) {
+ boot_ec_is_ecdt = false;
+ acpi_handle_debug(ec->handle, "duplicated.\n");
+ acpi_ec_free(ec);
+ ec = boot_ec;
+ ret = acpi_config_boot_ec(ec, ec->handle, true, false);
+ } else
+ ret = acpi_ec_setup(ec, true);
+ if (ret)
+ goto err_query;
- if (!first_ec)
- first_ec = ec;
device->driver_data = ec;
ret = !!request_region(ec->data_addr, 1, "EC data");
@@ -1412,20 +1602,17 @@ static int acpi_ec_add(struct acpi_device *device)
ret = !!request_region(ec->command_addr, 1, "EC cmd");
WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr);
- pr_info("GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n",
- ec->gpe, ec->command_addr, ec->data_addr);
-
- ret = ec_install_handlers(ec);
-
/* Reprobe devices depending on the EC */
acpi_walk_dep_device_list(ec->handle);
+ acpi_handle_debug(ec->handle, "enumerated.\n");
+ return 0;
- /* EC is fully operational, allow queries */
- clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
-
- /* Clear stale _Q events if hardware might require that */
- if (EC_FLAGS_CLEAR_ON_RESUME)
- acpi_ec_clear(ec);
+err_query:
+ if (ec != boot_ec)
+ acpi_ec_remove_query_handlers(ec, true, 0);
+err_alloc:
+ if (ec != boot_ec)
+ acpi_ec_free(ec);
return ret;
}
@@ -1437,14 +1624,13 @@ static int acpi_ec_remove(struct acpi_device *device)
return -EINVAL;
ec = acpi_driver_data(device);
- ec_remove_handlers(ec);
- acpi_ec_remove_query_handlers(ec, true, 0);
release_region(ec->data_addr, 1);
release_region(ec->command_addr, 1);
device->driver_data = NULL;
- if (ec == first_ec)
- first_ec = NULL;
- kfree(ec);
+ if (ec != boot_ec) {
+ ec_remove_handlers(ec);
+ acpi_ec_free(ec);
+ }
return 0;
}
@@ -1486,9 +1672,8 @@ int __init acpi_ec_dsdt_probe(void)
if (!ec)
return -ENOMEM;
/*
- * Finding EC from DSDT if there is no ECDT EC available. When this
- * function is invoked, ACPI tables have been fully loaded, we can
- * walk namespace now.
+ * At this point, the namespace is initialized, so start to find
+ * the namespace objects.
*/
status = acpi_get_devices(ec_device_ids[0].id,
ec_parse_device, ec, NULL);
@@ -1496,16 +1681,47 @@ int __init acpi_ec_dsdt_probe(void)
ret = -ENODEV;
goto error;
}
- ret = ec_install_handlers(ec);
-
+ /*
+ * When the DSDT EC is available, always re-configure boot EC to
+ * have _REG evaluated. _REG can only be evaluated after the
+ * namespace initialization.
+ * At this point, the GPE is not fully initialized, so do not to
+ * handle the events.
+ */
+ ret = acpi_config_boot_ec(ec, ec->handle, false, false);
error:
if (ret)
- kfree(ec);
- else
- first_ec = boot_ec = ec;
+ acpi_ec_free(ec);
return ret;
}
+/*
+ * If the DSDT EC is not functioning, we still need to prepare a fully
+ * functioning ECDT EC first in order to handle the events.
+ * https://bugzilla.kernel.org/show_bug.cgi?id=115021
+ */
+int __init acpi_ec_ecdt_start(void)
+{
+ acpi_handle handle;
+
+ if (!boot_ec)
+ return -ENODEV;
+ /*
+ * The DSDT EC should have already been started in
+ * acpi_ec_add().
+ */
+ if (!boot_ec_is_ecdt)
+ return -ENODEV;
+
+ /*
+ * At this point, the namespace and the GPE is initialized, so
+ * start to find the namespace objects and handle the events.
+ */
+ if (!acpi_ec_ecdt_get_handle(&handle))
+ return -ENODEV;
+ return acpi_config_boot_ec(boot_ec, handle, true, true);
+}
+
#if 0
/*
* Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not
@@ -1600,7 +1816,6 @@ int __init acpi_ec_ecdt_probe(void)
goto error;
}
- pr_info("EC description table is found, configuring boot EC\n");
if (EC_FLAGS_CORRECT_ECDT) {
ec->command_addr = ecdt_ptr->data.address;
ec->data_addr = ecdt_ptr->control.address;
@@ -1609,16 +1824,90 @@ int __init acpi_ec_ecdt_probe(void)
ec->data_addr = ecdt_ptr->data.address;
}
ec->gpe = ecdt_ptr->gpe;
- ec->handle = ACPI_ROOT_OBJECT;
- ret = ec_install_handlers(ec);
+
+ /*
+ * At this point, the namespace is not initialized, so do not find
+ * the namespace objects, or handle the events.
+ */
+ ret = acpi_config_boot_ec(ec, ACPI_ROOT_OBJECT, false, true);
error:
if (ret)
- kfree(ec);
- else
- first_ec = boot_ec = ec;
+ acpi_ec_free(ec);
return ret;
}
+#ifdef CONFIG_PM_SLEEP
+static void acpi_ec_enter_noirq(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ if (ec == first_ec) {
+ spin_lock_irqsave(&ec->lock, flags);
+ ec->saved_busy_polling = ec_busy_polling;
+ ec->saved_polling_guard = ec_polling_guard;
+ ec_busy_polling = true;
+ ec_polling_guard = 0;
+ ec_log_drv("interrupt blocked");
+ spin_unlock_irqrestore(&ec->lock, flags);
+ }
+}
+
+static void acpi_ec_leave_noirq(struct acpi_ec *ec)
+{
+ unsigned long flags;
+
+ if (ec == first_ec) {
+ spin_lock_irqsave(&ec->lock, flags);
+ ec_busy_polling = ec->saved_busy_polling;
+ ec_polling_guard = ec->saved_polling_guard;
+ ec_log_drv("interrupt unblocked");
+ spin_unlock_irqrestore(&ec->lock, flags);
+ }
+}
+
+static int acpi_ec_suspend_noirq(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ acpi_ec_enter_noirq(ec);
+ return 0;
+}
+
+static int acpi_ec_resume_noirq(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ acpi_ec_leave_noirq(ec);
+ return 0;
+}
+
+static int acpi_ec_suspend(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ if (ec_freeze_events)
+ acpi_ec_disable_event(ec);
+ return 0;
+}
+
+static int acpi_ec_resume(struct device *dev)
+{
+ struct acpi_ec *ec =
+ acpi_driver_data(to_acpi_device(dev));
+
+ acpi_ec_enable_event(ec);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops acpi_ec_pm = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq)
+ SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume)
+};
+
static int param_set_event_clearing(const char *val, struct kernel_param *kp)
{
int result = 0;
@@ -1664,6 +1953,7 @@ static struct acpi_driver acpi_ec_driver = {
.add = acpi_ec_add,
.remove = acpi_ec_remove,
},
+ .drv.pm = &acpi_ec_pm,
};
static inline int acpi_ec_query_init(void)
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 940218f..1b41a27 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -40,10 +40,8 @@ int acpi_sysfs_init(void);
void acpi_container_init(void);
void acpi_memory_hotplug_init(void);
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
-int acpi_ioapic_add(struct acpi_pci_root *root);
int acpi_ioapic_remove(struct acpi_pci_root *root);
#else
-static inline int acpi_ioapic_add(struct acpi_pci_root *root) { return 0; }
static inline int acpi_ioapic_remove(struct acpi_pci_root *root) { return 0; }
#endif
#ifdef CONFIG_ACPI_DOCK
@@ -116,7 +114,6 @@ bool acpi_device_is_present(struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev);
bool acpi_device_is_first_physical_node(struct acpi_device *adev,
const struct device *dev);
-struct device *acpi_get_first_physical_node(struct acpi_device *adev);
/* --------------------------------------------------------------------------
Device Matching and Notification
@@ -174,6 +171,8 @@ struct acpi_ec {
struct work_struct work;
unsigned long timestamp;
unsigned long nr_pending_queries;
+ bool saved_busy_polling;
+ unsigned int saved_polling_guard;
};
extern struct acpi_ec *first_ec;
@@ -185,9 +184,9 @@ typedef int (*acpi_ec_query_func) (void *data);
int acpi_ec_init(void);
int acpi_ec_ecdt_probe(void);
int acpi_ec_dsdt_probe(void);
+int acpi_ec_ecdt_start(void);
void acpi_ec_block_transactions(void);
void acpi_ec_unblock_transactions(void);
-void acpi_ec_unblock_transactions_early(void);
int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
acpi_handle handle, acpi_ec_query_func func,
void *data);
@@ -225,4 +224,14 @@ static inline void suspend_nvs_restore(void) {}
void acpi_init_properties(struct acpi_device *adev);
void acpi_free_properties(struct acpi_device *adev);
+/*--------------------------------------------------------------------------
+ Watchdog
+ -------------------------------------------------------------------------- */
+
+#ifdef CONFIG_ACPI_WATCHDOG
+void acpi_watchdog_init(void);
+#else
+static inline void acpi_watchdog_init(void) {}
+#endif
+
#endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/ioapic.c b/drivers/acpi/ioapic.c
index ccdc8db..6d7ce6e 100644
--- a/drivers/acpi/ioapic.c
+++ b/drivers/acpi/ioapic.c
@@ -46,7 +46,7 @@ static acpi_status setup_res(struct acpi_resource *acpi_res, void *data)
struct resource_win win;
res->flags = 0;
- if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM) == 0)
+ if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM))
return AE_OK;
if (!acpi_dev_resource_memory(acpi_res, res)) {
@@ -97,7 +97,7 @@ static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl,
unsigned long long gsi_base;
struct acpi_pci_ioapic *ioapic;
struct pci_dev *dev = NULL;
- struct resource *res = NULL;
+ struct resource *res = NULL, *pci_res = NULL, *crs_res;
char *type = NULL;
if (!acpi_is_ioapic(handle, &type))
@@ -137,23 +137,30 @@ static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl,
pci_set_master(dev);
if (pci_request_region(dev, 0, type))
goto exit_disable;
- res = &dev->resource[0];
+ pci_res = &dev->resource[0];
ioapic->pdev = dev;
} else {
pci_dev_put(dev);
dev = NULL;
+ }
- res = &ioapic->res;
- acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, res);
- if (res->flags == 0) {
- acpi_handle_warn(handle, "failed to get resource\n");
- goto exit_free;
- } else if (request_resource(&iomem_resource, res)) {
- acpi_handle_warn(handle, "failed to insert resource\n");
- goto exit_free;
- }
+ crs_res = &ioapic->res;
+ acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, crs_res);
+ crs_res->name = type;
+ crs_res->flags |= IORESOURCE_BUSY;
+ if (crs_res->flags == 0) {
+ acpi_handle_warn(handle, "failed to get resource\n");
+ goto exit_release;
+ } else if (insert_resource(&iomem_resource, crs_res)) {
+ acpi_handle_warn(handle, "failed to insert resource\n");
+ goto exit_release;
}
+ /* try pci resource first, then "_CRS" resource */
+ res = pci_res;
+ if (!res || !res->flags)
+ res = crs_res;
+
if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) {
acpi_handle_warn(handle, "failed to register IOAPIC\n");
goto exit_release;
@@ -174,14 +181,13 @@ done:
exit_release:
if (dev)
pci_release_region(dev, 0);
- else
- release_resource(res);
+ if (ioapic->res.flags && ioapic->res.parent)
+ release_resource(&ioapic->res);
exit_disable:
if (dev)
pci_disable_device(dev);
exit_put:
pci_dev_put(dev);
-exit_free:
kfree(ioapic);
exit:
mutex_unlock(&ioapic_list_lock);
@@ -189,13 +195,13 @@ exit:
return AE_OK;
}
-int acpi_ioapic_add(struct acpi_pci_root *root)
+int acpi_ioapic_add(acpi_handle root_handle)
{
acpi_status status, retval = AE_OK;
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle,
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root_handle,
UINT_MAX, handle_ioapic_add, NULL,
- root->device->handle, (void **)&retval);
+ root_handle, (void **)&retval);
return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV;
}
@@ -217,9 +223,9 @@ int acpi_ioapic_remove(struct acpi_pci_root *root)
pci_release_region(ioapic->pdev, 0);
pci_disable_device(ioapic->pdev);
pci_dev_put(ioapic->pdev);
- } else if (ioapic->res.flags && ioapic->res.parent) {
- release_resource(&ioapic->res);
}
+ if (ioapic->res.flags && ioapic->res.parent)
+ release_resource(&ioapic->res);
list_del(&ioapic->list);
kfree(ioapic);
}
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index 80cc7c0..e1d5ea6 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -94,54 +94,50 @@ static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc)
return to_acpi_device(acpi_desc->dev);
}
-static int xlat_status(void *buf, unsigned int cmd)
+static int xlat_status(void *buf, unsigned int cmd, u32 status)
{
struct nd_cmd_clear_error *clear_err;
struct nd_cmd_ars_status *ars_status;
- struct nd_cmd_ars_start *ars_start;
- struct nd_cmd_ars_cap *ars_cap;
u16 flags;
switch (cmd) {
case ND_CMD_ARS_CAP:
- ars_cap = buf;
- if ((ars_cap->status & 0xffff) == NFIT_ARS_CAP_NONE)
+ if ((status & 0xffff) == NFIT_ARS_CAP_NONE)
return -ENOTTY;
/* Command failed */
- if (ars_cap->status & 0xffff)
+ if (status & 0xffff)
return -EIO;
/* No supported scan types for this range */
flags = ND_ARS_PERSISTENT | ND_ARS_VOLATILE;
- if ((ars_cap->status >> 16 & flags) == 0)
+ if ((status >> 16 & flags) == 0)
return -ENOTTY;
break;
case ND_CMD_ARS_START:
- ars_start = buf;
/* ARS is in progress */
- if ((ars_start->status & 0xffff) == NFIT_ARS_START_BUSY)
+ if ((status & 0xffff) == NFIT_ARS_START_BUSY)
return -EBUSY;
/* Command failed */
- if (ars_start->status & 0xffff)
+ if (status & 0xffff)
return -EIO;
break;
case ND_CMD_ARS_STATUS:
ars_status = buf;
/* Command failed */
- if (ars_status->status & 0xffff)
+ if (status & 0xffff)
return -EIO;
/* Check extended status (Upper two bytes) */
- if (ars_status->status == NFIT_ARS_STATUS_DONE)
+ if (status == NFIT_ARS_STATUS_DONE)
return 0;
/* ARS is in progress */
- if (ars_status->status == NFIT_ARS_STATUS_BUSY)
+ if (status == NFIT_ARS_STATUS_BUSY)
return -EBUSY;
/* No ARS performed for the current boot */
- if (ars_status->status == NFIT_ARS_STATUS_NONE)
+ if (status == NFIT_ARS_STATUS_NONE)
return -EAGAIN;
/*
@@ -149,19 +145,19 @@ static int xlat_status(void *buf, unsigned int cmd)
* agent wants the scan to stop. If we didn't overflow
* then just continue with the returned results.
*/
- if (ars_status->status == NFIT_ARS_STATUS_INTR) {
+ if (status == NFIT_ARS_STATUS_INTR) {
if (ars_status->flags & NFIT_ARS_F_OVERFLOW)
return -ENOSPC;
return 0;
}
/* Unknown status */
- if (ars_status->status >> 16)
+ if (status >> 16)
return -EIO;
break;
case ND_CMD_CLEAR_ERROR:
clear_err = buf;
- if (clear_err->status & 0xffff)
+ if (status & 0xffff)
return -EIO;
if (!clear_err->cleared)
return -EIO;
@@ -172,6 +168,9 @@ static int xlat_status(void *buf, unsigned int cmd)
break;
}
+ /* all other non-zero status results in an error */
+ if (status)
+ return -EIO;
return 0;
}
@@ -186,10 +185,10 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
struct nd_cmd_pkg *call_pkg = NULL;
const char *cmd_name, *dimm_name;
unsigned long cmd_mask, dsm_mask;
+ u32 offset, fw_status = 0;
acpi_handle handle;
unsigned int func;
const u8 *uuid;
- u32 offset;
int rc, i;
func = cmd;
@@ -317,6 +316,15 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
out_obj->buffer.pointer + offset, out_size);
offset += out_size;
}
+
+ /*
+ * Set fw_status for all the commands with a known format to be
+ * later interpreted by xlat_status().
+ */
+ if (i >= 1 && ((cmd >= ND_CMD_ARS_CAP && cmd <= ND_CMD_CLEAR_ERROR)
+ || (cmd >= ND_CMD_SMART && cmd <= ND_CMD_VENDOR)))
+ fw_status = *(u32 *) out_obj->buffer.pointer;
+
if (offset + in_buf.buffer.length < buf_len) {
if (i >= 1) {
/*
@@ -325,7 +333,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
*/
rc = buf_len - offset - in_buf.buffer.length;
if (cmd_rc)
- *cmd_rc = xlat_status(buf, cmd);
+ *cmd_rc = xlat_status(buf, cmd, fw_status);
} else {
dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n",
__func__, dimm_name, cmd_name, buf_len,
@@ -335,7 +343,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc,
} else {
rc = 0;
if (cmd_rc)
- *cmd_rc = xlat_status(buf, cmd);
+ *cmd_rc = xlat_status(buf, cmd, fw_status);
}
out:
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index 2c45dd3..c576a6f 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -411,7 +411,15 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
int gsi;
u8 pin;
int triggering = ACPI_LEVEL_SENSITIVE;
- int polarity = ACPI_ACTIVE_LOW;
+ /*
+ * On ARM systems with the GIC interrupt model, level interrupts
+ * are always polarity high by specification; PCI legacy
+ * IRQs lines are inverted before reaching the interrupt
+ * controller and must therefore be considered active high
+ * as default.
+ */
+ int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ?
+ ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW;
char *link = NULL;
char link_desc[16];
int rc;
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index d144168..bf601d4 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -614,7 +614,17 @@ static int acpi_pci_root_add(struct acpi_device *device,
if (hotadd) {
pcibios_resource_survey_bus(root->bus);
pci_assign_unassigned_root_bus_resources(root->bus);
- acpi_ioapic_add(root);
+ /*
+ * This is only called for the hotadd case. For the boot-time
+ * case, we need to wait until after PCI initialization in
+ * order to deal with IOAPICs mapped in on a PCI BAR.
+ *
+ * This is currently x86-specific, because acpi_ioapic_add()
+ * is an empty function without CONFIG_ACPI_HOTPLUG_IOAPIC.
+ * And CONFIG_ACPI_HOTPLUG_IOAPIC depends on CONFIG_X86_IO_APIC
+ * (see drivers/acpi/Kconfig).
+ */
+ acpi_ioapic_add(root->device->handle);
}
pci_lock_rescan_remove();
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 9125d7d..5c78ee1 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -32,12 +32,12 @@ static struct acpi_table_madt *get_madt_table(void)
}
static int map_lapic_id(struct acpi_subtable_header *entry,
- u32 acpi_id, phys_cpuid_t *apic_id)
+ u32 acpi_id, phys_cpuid_t *apic_id, bool ignore_disabled)
{
struct acpi_madt_local_apic *lapic =
container_of(entry, struct acpi_madt_local_apic, header);
- if (!(lapic->lapic_flags & ACPI_MADT_ENABLED))
+ if (ignore_disabled && !(lapic->lapic_flags & ACPI_MADT_ENABLED))
return -ENODEV;
if (lapic->processor_id != acpi_id)
@@ -48,12 +48,13 @@ static int map_lapic_id(struct acpi_subtable_header *entry,
}
static int map_x2apic_id(struct acpi_subtable_header *entry,
- int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
+ int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id,
+ bool ignore_disabled)
{
struct acpi_madt_local_x2apic *apic =
container_of(entry, struct acpi_madt_local_x2apic, header);
- if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
+ if (ignore_disabled && !(apic->lapic_flags & ACPI_MADT_ENABLED))
return -ENODEV;
if (device_declaration && (apic->uid == acpi_id)) {
@@ -65,12 +66,13 @@ static int map_x2apic_id(struct acpi_subtable_header *entry,
}
static int map_lsapic_id(struct acpi_subtable_header *entry,
- int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
+ int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id,
+ bool ignore_disabled)
{
struct acpi_madt_local_sapic *lsapic =
container_of(entry, struct acpi_madt_local_sapic, header);
- if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))
+ if (ignore_disabled && !(lsapic->lapic_flags & ACPI_MADT_ENABLED))
return -ENODEV;
if (device_declaration) {
@@ -87,12 +89,13 @@ static int map_lsapic_id(struct acpi_subtable_header *entry,
* Retrieve the ARM CPU physical identifier (MPIDR)
*/
static int map_gicc_mpidr(struct acpi_subtable_header *entry,
- int device_declaration, u32 acpi_id, phys_cpuid_t *mpidr)
+ int device_declaration, u32 acpi_id, phys_cpuid_t *mpidr,
+ bool ignore_disabled)
{
struct acpi_madt_generic_interrupt *gicc =
container_of(entry, struct acpi_madt_generic_interrupt, header);
- if (!(gicc->flags & ACPI_MADT_ENABLED))
+ if (ignore_disabled && !(gicc->flags & ACPI_MADT_ENABLED))
return -ENODEV;
/* device_declaration means Device object in DSDT, in the
@@ -109,7 +112,7 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry,
}
static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
- int type, u32 acpi_id)
+ int type, u32 acpi_id, bool ignore_disabled)
{
unsigned long madt_end, entry;
phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */
@@ -127,16 +130,20 @@ static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
struct acpi_subtable_header *header =
(struct acpi_subtable_header *)entry;
if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) {
- if (!map_lapic_id(header, acpi_id, &phys_id))
+ if (!map_lapic_id(header, acpi_id, &phys_id,
+ ignore_disabled))
break;
} else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) {
- if (!map_x2apic_id(header, type, acpi_id, &phys_id))
+ if (!map_x2apic_id(header, type, acpi_id, &phys_id,
+ ignore_disabled))
break;
} else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
- if (!map_lsapic_id(header, type, acpi_id, &phys_id))
+ if (!map_lsapic_id(header, type, acpi_id, &phys_id,
+ ignore_disabled))
break;
} else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
- if (!map_gicc_mpidr(header, type, acpi_id, &phys_id))
+ if (!map_gicc_mpidr(header, type, acpi_id, &phys_id,
+ ignore_disabled))
break;
}
entry += header->length;
@@ -156,14 +163,15 @@ phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id)
if (!madt)
return PHYS_CPUID_INVALID;
- rv = map_madt_entry(madt, 1, acpi_id);
+ rv = map_madt_entry(madt, 1, acpi_id, true);
early_acpi_os_unmap_memory(madt, tbl_size);
return rv;
}
-static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
+static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id,
+ bool ignore_disabled)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
@@ -184,30 +192,38 @@ static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
header = (struct acpi_subtable_header *)obj->buffer.pointer;
if (header->type == ACPI_MADT_TYPE_LOCAL_APIC)
- map_lapic_id(header, acpi_id, &phys_id);
+ map_lapic_id(header, acpi_id, &phys_id, ignore_disabled);
else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC)
- map_lsapic_id(header, type, acpi_id, &phys_id);
+ map_lsapic_id(header, type, acpi_id, &phys_id, ignore_disabled);
else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC)
- map_x2apic_id(header, type, acpi_id, &phys_id);
+ map_x2apic_id(header, type, acpi_id, &phys_id, ignore_disabled);
else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT)
- map_gicc_mpidr(header, type, acpi_id, &phys_id);
+ map_gicc_mpidr(header, type, acpi_id, &phys_id,
+ ignore_disabled);
exit:
kfree(buffer.pointer);
return phys_id;
}
-phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
+static phys_cpuid_t __acpi_get_phys_id(acpi_handle handle, int type,
+ u32 acpi_id, bool ignore_disabled)
{
phys_cpuid_t phys_id;
- phys_id = map_mat_entry(handle, type, acpi_id);
+ phys_id = map_mat_entry(handle, type, acpi_id, ignore_disabled);
if (invalid_phys_cpuid(phys_id))
- phys_id = map_madt_entry(get_madt_table(), type, acpi_id);
+ phys_id = map_madt_entry(get_madt_table(), type, acpi_id,
+ ignore_disabled);
return phys_id;
}
+phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
+{
+ return __acpi_get_phys_id(handle, type, acpi_id, true);
+}
+
int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
{
#ifdef CONFIG_SMP
@@ -264,6 +280,79 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
}
EXPORT_SYMBOL_GPL(acpi_get_cpuid);
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+static bool __init
+map_processor(acpi_handle handle, phys_cpuid_t *phys_id, int *cpuid)
+{
+ int type, id;
+ u32 acpi_id;
+ acpi_status status;
+ acpi_object_type acpi_type;
+ unsigned long long tmp;
+ union acpi_object object = { 0 };
+ struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
+
+ status = acpi_get_type(handle, &acpi_type);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ switch (acpi_type) {
+ case ACPI_TYPE_PROCESSOR:
+ status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return false;
+ acpi_id = object.processor.proc_id;
+
+ /* validate the acpi_id */
+ if(acpi_processor_validate_proc_id(acpi_id))
+ return false;
+ break;
+ case ACPI_TYPE_DEVICE:
+ status = acpi_evaluate_integer(handle, "_UID", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return false;
+ acpi_id = tmp;
+ break;
+ default:
+ return false;
+ }
+
+ type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0;
+
+ *phys_id = __acpi_get_phys_id(handle, type, acpi_id, false);
+ id = acpi_map_cpuid(*phys_id, acpi_id);
+
+ if (id < 0)
+ return false;
+ *cpuid = id;
+ return true;
+}
+
+static acpi_status __init
+set_processor_node_mapping(acpi_handle handle, u32 lvl, void *context,
+ void **rv)
+{
+ phys_cpuid_t phys_id;
+ int cpu_id;
+
+ if (!map_processor(handle, &phys_id, &cpu_id))
+ return AE_ERROR;
+
+ acpi_map_cpu2node(handle, cpu_id, phys_id);
+ return AE_OK;
+}
+
+void __init acpi_set_processor_mapping(void)
+{
+ /* Set persistent cpu <-> node mapping for all processors. */
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, set_processor_node_mapping,
+ NULL, NULL, NULL);
+}
+#else
+void __init acpi_set_processor_mapping(void) {}
+#endif /* CONFIG_ACPI_HOTPLUG_CPU */
+
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base,
u64 *phys_addr, int *ioapic_id)
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 0553aee..9d5f0c7 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -110,55 +110,46 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
static int __acpi_processor_start(struct acpi_device *device);
-static int acpi_cpu_soft_notify(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
+static int acpi_soft_cpu_online(unsigned int cpu)
{
- unsigned int cpu = (unsigned long)hcpu;
struct acpi_processor *pr = per_cpu(processors, cpu);
struct acpi_device *device;
- action &= ~CPU_TASKS_FROZEN;
-
- switch (action) {
- case CPU_ONLINE:
- case CPU_DEAD:
- break;
- default:
- return NOTIFY_DONE;
- }
if (!pr || acpi_bus_get_device(pr->handle, &device))
- return NOTIFY_DONE;
-
- if (action == CPU_ONLINE) {
- /*
- * CPU got physically hotplugged and onlined for the first time:
- * Initialize missing things.
- */
- if (pr->flags.need_hotplug_init) {
- int ret;
-
- pr_info("Will online and init hotplugged CPU: %d\n",
- pr->id);
- pr->flags.need_hotplug_init = 0;
- ret = __acpi_processor_start(device);
- WARN(ret, "Failed to start CPU: %d\n", pr->id);
- } else {
- /* Normal CPU soft online event. */
- acpi_processor_ppc_has_changed(pr, 0);
- acpi_processor_hotplug(pr);
- acpi_processor_reevaluate_tstate(pr, action);
- acpi_processor_tstate_has_changed(pr);
- }
- } else if (action == CPU_DEAD) {
- /* Invalidate flag.throttling after the CPU is offline. */
- acpi_processor_reevaluate_tstate(pr, action);
+ return 0;
+ /*
+ * CPU got physically hotplugged and onlined for the first time:
+ * Initialize missing things.
+ */
+ if (pr->flags.need_hotplug_init) {
+ int ret;
+
+ pr_info("Will online and init hotplugged CPU: %d\n",
+ pr->id);
+ pr->flags.need_hotplug_init = 0;
+ ret = __acpi_processor_start(device);
+ WARN(ret, "Failed to start CPU: %d\n", pr->id);
+ } else {
+ /* Normal CPU soft online event. */
+ acpi_processor_ppc_has_changed(pr, 0);
+ acpi_processor_hotplug(pr);
+ acpi_processor_reevaluate_tstate(pr, false);
+ acpi_processor_tstate_has_changed(pr);
}
- return NOTIFY_OK;
+ return 0;
}
-static struct notifier_block acpi_cpu_notifier = {
- .notifier_call = acpi_cpu_soft_notify,
-};
+static int acpi_soft_cpu_dead(unsigned int cpu)
+{
+ struct acpi_processor *pr = per_cpu(processors, cpu);
+ struct acpi_device *device;
+
+ if (!pr || acpi_bus_get_device(pr->handle, &device))
+ return 0;
+
+ acpi_processor_reevaluate_tstate(pr, true);
+ return 0;
+}
#ifdef CONFIG_ACPI_CPU_FREQ_PSS
static int acpi_pss_perf_init(struct acpi_processor *pr,
@@ -245,8 +236,8 @@ static int __acpi_processor_start(struct acpi_device *device)
return 0;
result = acpi_cppc_processor_probe(pr);
- if (result)
- return -ENODEV;
+ if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
+ dev_warn(&device->dev, "CPPC data invalid or not present\n");
if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
acpi_processor_power_init(pr);
@@ -303,7 +294,7 @@ static int acpi_processor_stop(struct device *dev)
* This is needed for the powernow-k8 driver, that works even without
* ACPI, but needs symbols from this driver
*/
-
+static enum cpuhp_state hp_online;
static int __init acpi_processor_driver_init(void)
{
int result = 0;
@@ -315,11 +306,22 @@ static int __init acpi_processor_driver_init(void)
if (result < 0)
return result;
- register_hotcpu_notifier(&acpi_cpu_notifier);
+ result = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "acpi/cpu-drv:online",
+ acpi_soft_cpu_online, NULL);
+ if (result < 0)
+ goto err;
+ hp_online = result;
+ cpuhp_setup_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD, "acpi/cpu-drv:dead",
+ NULL, acpi_soft_cpu_dead);
+
acpi_thermal_cpufreq_init();
acpi_processor_ppc_init();
acpi_processor_throttling_init();
return 0;
+err:
+ driver_unregister(&acpi_processor_driver);
+ return result;
}
static void __exit acpi_processor_driver_exit(void)
@@ -329,7 +331,8 @@ static void __exit acpi_processor_driver_exit(void)
acpi_processor_ppc_exit();
acpi_thermal_cpufreq_exit();
- unregister_hotcpu_notifier(&acpi_cpu_notifier);
+ cpuhp_remove_state_nocalls(hp_online);
+ cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
driver_unregister(&acpi_processor_driver);
}
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index c72e648..d51ca1c 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -375,11 +375,11 @@ int acpi_processor_tstate_has_changed(struct acpi_processor *pr)
* 3. TSD domain
*/
void acpi_processor_reevaluate_tstate(struct acpi_processor *pr,
- unsigned long action)
+ bool is_dead)
{
int result = 0;
- if (action == CPU_DEAD) {
+ if (is_dead) {
/* When one CPU is offline, the T-state throttling
* will be invalidated.
*/
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index e878fc7..035ac64 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2002,6 +2002,7 @@ int __init acpi_scan_init(void)
acpi_pnp_init();
acpi_int340x_thermal_init();
acpi_amba_init();
+ acpi_watchdog_init();
acpi_scan_add_handler(&generic_device_handler);
@@ -2044,6 +2045,7 @@ int __init acpi_scan_init(void)
}
acpi_update_all_gpes();
+ acpi_ec_ecdt_start();
acpi_scan_initialized = true;
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 2b38c1b..deb0ff7 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -572,7 +572,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
- if (pwr_btn_status & ACPI_EVENT_FLAG_SET) {
+ if (pwr_btn_status & ACPI_EVENT_FLAG_STATUS_SET) {
acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
/* Flag for later */
pwr_btn_event_pending = true;
@@ -586,7 +586,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
*/
acpi_disable_all_gpes();
/* Allow EC transactions to happen. */
- acpi_ec_unblock_transactions_early();
+ acpi_ec_unblock_transactions();
suspend_nvs_restore();
@@ -784,7 +784,7 @@ static void acpi_hibernation_leave(void)
/* Restore the NVS memory area */
suspend_nvs_restore();
/* Allow EC transactions to happen. */
- acpi_ec_unblock_transactions_early();
+ acpi_ec_unblock_transactions();
}
static void acpi_pm_thaw(void)
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
new file mode 100644
index 0000000..e8d7bc7
--- /dev/null
+++ b/drivers/acpi/spcr.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ * Copyright (c) 2015, Red Hat, Inc.
+ * Copyright (c) 2015, 2016 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) "ACPI: SPCR: " fmt
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/kernel.h>
+#include <linux/serial_core.h>
+
+/**
+ * parse_spcr() - parse ACPI SPCR table and add preferred console
+ *
+ * @earlycon: set up earlycon for the console specified by the table
+ *
+ * For the architectures with support for ACPI, CONFIG_ACPI_SPCR_TABLE may be
+ * defined to parse ACPI SPCR table. As a result of the parsing preferred
+ * console is registered and if @earlycon is true, earlycon is set up.
+ *
+ * When CONFIG_ACPI_SPCR_TABLE is defined, this function should be called
+ * from arch inintialization code as soon as the DT/ACPI decision is made.
+ *
+ */
+int __init parse_spcr(bool earlycon)
+{
+ static char opts[64];
+ struct acpi_table_spcr *table;
+ acpi_size table_size;
+ acpi_status status;
+ char *uart;
+ char *iotype;
+ int baud_rate;
+ int err;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ status = acpi_get_table_with_size(ACPI_SIG_SPCR, 0,
+ (struct acpi_table_header **)&table,
+ &table_size);
+
+ if (ACPI_FAILURE(status))
+ return -ENOENT;
+
+ if (table->header.revision < 2) {
+ err = -ENOENT;
+ pr_err("wrong table version\n");
+ goto done;
+ }
+
+ iotype = table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY ?
+ "mmio" : "io";
+
+ switch (table->interface_type) {
+ case ACPI_DBG2_ARM_SBSA_32BIT:
+ iotype = "mmio32";
+ /* fall through */
+ case ACPI_DBG2_ARM_PL011:
+ case ACPI_DBG2_ARM_SBSA_GENERIC:
+ case ACPI_DBG2_BCM2835:
+ uart = "pl011";
+ break;
+ case ACPI_DBG2_16550_COMPATIBLE:
+ case ACPI_DBG2_16550_SUBSET:
+ uart = "uart";
+ break;
+ default:
+ err = -ENOENT;
+ goto done;
+ }
+
+ switch (table->baud_rate) {
+ case 3:
+ baud_rate = 9600;
+ break;
+ case 4:
+ baud_rate = 19200;
+ break;
+ case 6:
+ baud_rate = 57600;
+ break;
+ case 7:
+ baud_rate = 115200;
+ break;
+ default:
+ err = -ENOENT;
+ goto done;
+ }
+
+ snprintf(opts, sizeof(opts), "%s,%s,0x%llx,%d", uart, iotype,
+ table->serial_port.address, baud_rate);
+
+ pr_info("console: %s\n", opts);
+
+ if (earlycon)
+ setup_earlycon(opts);
+
+ err = add_preferred_console(uart, 0, opts + strlen(uart) + 1);
+
+done:
+ early_acpi_os_unmap_memory((void __iomem *)table, table_size);
+ return err;
+}
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 358165e..703c26e 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -314,10 +314,14 @@ static struct kobject *tables_kobj;
static struct kobject *dynamic_tables_kobj;
static struct kobject *hotplug_kobj;
+#define ACPI_MAX_TABLE_INSTANCES 999
+#define ACPI_INST_SIZE 4 /* including trailing 0 */
+
struct acpi_table_attr {
struct bin_attribute attr;
- char name[8];
+ char name[ACPI_NAME_SIZE];
int instance;
+ char filename[ACPI_NAME_SIZE+ACPI_INST_SIZE];
struct list_head node;
};
@@ -329,14 +333,9 @@ static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,
container_of(bin_attr, struct acpi_table_attr, attr);
struct acpi_table_header *table_header = NULL;
acpi_status status;
- char name[ACPI_NAME_SIZE];
-
- if (strncmp(table_attr->name, "NULL", 4))
- memcpy(name, table_attr->name, ACPI_NAME_SIZE);
- else
- memcpy(name, "\0\0\0\0", 4);
- status = acpi_get_table(name, table_attr->instance, &table_header);
+ status = acpi_get_table(table_attr->name, table_attr->instance,
+ &table_header);
if (ACPI_FAILURE(status))
return -ENODEV;
@@ -344,38 +343,45 @@ static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,
table_header, table_header->length);
}
-static void acpi_table_attr_init(struct acpi_table_attr *table_attr,
- struct acpi_table_header *table_header)
+static int acpi_table_attr_init(struct kobject *tables_obj,
+ struct acpi_table_attr *table_attr,
+ struct acpi_table_header *table_header)
{
struct acpi_table_header *header = NULL;
struct acpi_table_attr *attr = NULL;
+ char instance_str[ACPI_INST_SIZE];
sysfs_attr_init(&table_attr->attr.attr);
- if (table_header->signature[0] != '\0')
- memcpy(table_attr->name, table_header->signature,
- ACPI_NAME_SIZE);
- else
- memcpy(table_attr->name, "NULL", 4);
+ ACPI_MOVE_NAME(table_attr->name, table_header->signature);
list_for_each_entry(attr, &acpi_table_attr_list, node) {
- if (!memcmp(table_attr->name, attr->name, ACPI_NAME_SIZE))
+ if (ACPI_COMPARE_NAME(table_attr->name, attr->name))
if (table_attr->instance < attr->instance)
table_attr->instance = attr->instance;
}
table_attr->instance++;
+ if (table_attr->instance > ACPI_MAX_TABLE_INSTANCES) {
+ pr_warn("%4.4s: too many table instances\n",
+ table_attr->name);
+ return -ERANGE;
+ }
+ ACPI_MOVE_NAME(table_attr->filename, table_header->signature);
+ table_attr->filename[ACPI_NAME_SIZE] = '\0';
if (table_attr->instance > 1 || (table_attr->instance == 1 &&
!acpi_get_table
- (table_header->signature, 2, &header)))
- sprintf(table_attr->name + ACPI_NAME_SIZE, "%d",
- table_attr->instance);
+ (table_header->signature, 2, &header))) {
+ snprintf(instance_str, sizeof(instance_str), "%u",
+ table_attr->instance);
+ strcat(table_attr->filename, instance_str);
+ }
table_attr->attr.size = table_header->length;
table_attr->attr.read = acpi_table_show;
- table_attr->attr.attr.name = table_attr->name;
+ table_attr->attr.attr.name = table_attr->filename;
table_attr->attr.attr.mode = 0400;
- return;
+ return sysfs_create_bin_file(tables_obj, &table_attr->attr);
}
acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
@@ -383,21 +389,22 @@ acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
struct acpi_table_attr *table_attr;
switch (event) {
- case ACPI_TABLE_EVENT_LOAD:
+ case ACPI_TABLE_EVENT_INSTALL:
table_attr =
kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL);
if (!table_attr)
return AE_NO_MEMORY;
- acpi_table_attr_init(table_attr, table);
- if (sysfs_create_bin_file(dynamic_tables_kobj,
- &table_attr->attr)) {
+ if (acpi_table_attr_init(dynamic_tables_kobj,
+ table_attr, table)) {
kfree(table_attr);
return AE_ERROR;
- } else
- list_add_tail(&table_attr->node, &acpi_table_attr_list);
+ }
+ list_add_tail(&table_attr->node, &acpi_table_attr_list);
break;
+ case ACPI_TABLE_EVENT_LOAD:
case ACPI_TABLE_EVENT_UNLOAD:
+ case ACPI_TABLE_EVENT_UNINSTALL:
/*
* we do not need to do anything right now
* because the table is not deleted from the
@@ -435,13 +442,12 @@ static int acpi_tables_sysfs_init(void)
if (ACPI_FAILURE(status))
continue;
- table_attr = NULL;
table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);
if (!table_attr)
return -ENOMEM;
- acpi_table_attr_init(table_attr, table_header);
- ret = sysfs_create_bin_file(tables_kobj, &table_attr->attr);
+ ret = acpi_table_attr_init(tables_kobj,
+ table_attr, table_header);
if (ret) {
kfree(table_attr);
return ret;
@@ -597,14 +603,27 @@ static ssize_t counter_show(struct kobject *kobj,
if (result)
goto end;
+ if (status & ACPI_EVENT_FLAG_ENABLE_SET)
+ size += sprintf(buf + size, " EN");
+ else
+ size += sprintf(buf + size, " ");
+ if (status & ACPI_EVENT_FLAG_STATUS_SET)
+ size += sprintf(buf + size, " STS");
+ else
+ size += sprintf(buf + size, " ");
+
if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER))
- size += sprintf(buf + size, " invalid");
+ size += sprintf(buf + size, " invalid ");
else if (status & ACPI_EVENT_FLAG_ENABLED)
- size += sprintf(buf + size, " enabled");
+ size += sprintf(buf + size, " enabled ");
else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED)
- size += sprintf(buf + size, " wake_enabled");
+ size += sprintf(buf + size, " wake_enabled");
+ else
+ size += sprintf(buf + size, " disabled ");
+ if (status & ACPI_EVENT_FLAG_MASKED)
+ size += sprintf(buf + size, " masked ");
else
- size += sprintf(buf + size, " disabled");
+ size += sprintf(buf + size, " unmasked");
end:
size += sprintf(buf + size, "\n");
@@ -655,8 +674,12 @@ static ssize_t counter_set(struct kobject *kobj,
!(status & ACPI_EVENT_FLAG_ENABLED))
result = acpi_enable_gpe(handle, index);
else if (!strcmp(buf, "clear\n") &&
- (status & ACPI_EVENT_FLAG_SET))
+ (status & ACPI_EVENT_FLAG_STATUS_SET))
result = acpi_clear_gpe(handle, index);
+ else if (!strcmp(buf, "mask\n"))
+ result = acpi_mask_gpe(handle, index, TRUE);
+ else if (!strcmp(buf, "unmask\n"))
+ result = acpi_mask_gpe(handle, index, FALSE);
else if (!kstrtoul(buf, 0, &tmp))
all_counters[index].count = tmp;
else
@@ -664,13 +687,13 @@ static ssize_t counter_set(struct kobject *kobj,
} else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) {
int event = index - num_gpes;
if (!strcmp(buf, "disable\n") &&
- (status & ACPI_EVENT_FLAG_ENABLED))
+ (status & ACPI_EVENT_FLAG_ENABLE_SET))
result = acpi_disable_event(event, ACPI_NOT_ISR);
else if (!strcmp(buf, "enable\n") &&
- !(status & ACPI_EVENT_FLAG_ENABLED))
+ !(status & ACPI_EVENT_FLAG_ENABLE_SET))
result = acpi_enable_event(event, ACPI_NOT_ISR);
else if (!strcmp(buf, "clear\n") &&
- (status & ACPI_EVENT_FLAG_SET))
+ (status & ACPI_EVENT_FLAG_STATUS_SET))
result = acpi_clear_event(event);
else if (!kstrtoul(buf, 0, &tmp))
all_counters[index].count = tmp;
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index 9f0ad6e..cdd56c4 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -35,7 +35,6 @@
#include <linux/earlycpio.h>
#include <linux/memblock.h>
#include <linux/initrd.h>
-#include <linux/acpi.h>
#include "internal.h"
#ifdef CONFIG_ACPI_CUSTOM_DSDT
@@ -246,6 +245,7 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
struct acpi_subtable_header *entry;
unsigned long table_end;
int count = 0;
+ int errs = 0;
int i;
if (acpi_disabled)
@@ -278,10 +278,12 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
if (entry->type != proc[i].id)
continue;
if (!proc[i].handler ||
- proc[i].handler(entry, table_end))
- return -EINVAL;
+ (!errs && proc[i].handler(entry, table_end))) {
+ errs++;
+ continue;
+ }
- proc->count++;
+ proc[i].count++;
break;
}
if (i != proc_num)
@@ -301,11 +303,11 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
}
if (max_entries && count > max_entries) {
- pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n",
- id, proc->id, count - max_entries, count);
+ pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n",
+ id, proc->id, count);
}
- return count;
+ return errs ? -EINVAL : count;
}
int __init
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 98504ec..fdf44ca 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -212,6 +212,16 @@ config DEBUG_DEVRES
If you are unsure about this, Say N here.
+config DEBUG_TEST_DRIVER_REMOVE
+ bool "Test driver remove calls during probe"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here if you want the Driver core to test driver remove functions
+ by calling probe, remove, probe. This tests the remove path without
+ having to unbind the driver or unload the driver module.
+
+ If you are unsure about this, say N here.
+
config SYS_HYPERVISOR
bool
default n
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
index 2ba4cac..95e3ef8 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -243,7 +243,7 @@ attribute_container_remove_device(struct device *dev,
* @dev: The generic device to run the trigger for
* @fn the function to execute for each classdev.
*
- * This funcion is for executing a trigger when you need to know both
+ * This function is for executing a trigger when you need to know both
* the container and the classdev. If you only care about the
* container, then use attribute_container_trigger() instead.
*/
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 0a8bdad..ce057a5 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -836,11 +836,29 @@ static struct kobject *get_device_parent(struct device *dev,
return NULL;
}
+static inline bool live_in_glue_dir(struct kobject *kobj,
+ struct device *dev)
+{
+ if (!kobj || !dev->class ||
+ kobj->kset != &dev->class->p->glue_dirs)
+ return false;
+ return true;
+}
+
+static inline struct kobject *get_glue_dir(struct device *dev)
+{
+ return dev->kobj.parent;
+}
+
+/*
+ * make sure cleaning up dir as the last step, we need to make
+ * sure .release handler of kobject is run with holding the
+ * global lock
+ */
static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
{
/* see if we live in a "glue" directory */
- if (!glue_dir || !dev->class ||
- glue_dir->kset != &dev->class->p->glue_dirs)
+ if (!live_in_glue_dir(glue_dir, dev))
return;
mutex_lock(&gdp_mutex);
@@ -848,11 +866,6 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
mutex_unlock(&gdp_mutex);
}
-static void cleanup_device_parent(struct device *dev)
-{
- cleanup_glue_dir(dev, dev->kobj.parent);
-}
-
static int device_add_class_symlinks(struct device *dev)
{
struct device_node *of_node = dev_of_node(dev);
@@ -1028,6 +1041,7 @@ int device_add(struct device *dev)
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
+ struct kobject *glue_dir = NULL;
dev = get_device(dev);
if (!dev)
@@ -1072,8 +1086,10 @@ int device_add(struct device *dev)
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
- if (error)
+ if (error) {
+ glue_dir = get_glue_dir(dev);
goto Error;
+ }
/* notify platform of device entry */
if (platform_notify)
@@ -1154,9 +1170,10 @@ done:
device_remove_file(dev, &dev_attr_uevent);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
+ glue_dir = get_glue_dir(dev);
kobject_del(&dev->kobj);
Error:
- cleanup_device_parent(dev);
+ cleanup_glue_dir(dev, glue_dir);
put_device(parent);
name_error:
kfree(dev->p);
@@ -1232,6 +1249,7 @@ EXPORT_SYMBOL_GPL(put_device);
void device_del(struct device *dev)
{
struct device *parent = dev->parent;
+ struct kobject *glue_dir = NULL;
struct class_interface *class_intf;
/* Notify clients of device removal. This call must come
@@ -1266,6 +1284,7 @@ void device_del(struct device *dev)
bus_remove_device(dev);
device_pm_remove(dev);
driver_deferred_probe_del(dev);
+ device_remove_properties(dev);
/* Notify the platform of the removal, in case they
* need to do anything...
@@ -1276,8 +1295,9 @@ void device_del(struct device *dev)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_REMOVED_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
- cleanup_device_parent(dev);
+ glue_dir = get_glue_dir(dev);
kobject_del(&dev->kobj);
+ cleanup_glue_dir(dev, glue_dir);
put_device(parent);
}
EXPORT_SYMBOL_GPL(device_del);
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 691eeea..4c28e1a 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -371,12 +371,13 @@ int register_cpu(struct cpu *cpu, int num)
if (cpu->hotpluggable)
cpu->dev.groups = hotplugable_cpu_attr_groups;
error = device_register(&cpu->dev);
- if (!error)
- per_cpu(cpu_sys_devices, num) = &cpu->dev;
- if (!error)
- register_cpu_under_node(num, cpu_to_node(num));
+ if (error)
+ return error;
- return error;
+ per_cpu(cpu_sys_devices, num) = &cpu->dev;
+ register_cpu_under_node(num, cpu_to_node(num));
+
+ return 0;
}
struct device *get_cpu_device(unsigned cpu)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 16688f5..d22a726 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -51,7 +51,6 @@
static DEFINE_MUTEX(deferred_probe_mutex);
static LIST_HEAD(deferred_probe_pending_list);
static LIST_HEAD(deferred_probe_active_list);
-static struct workqueue_struct *deferred_wq;
static atomic_t deferred_trigger_count = ATOMIC_INIT(0);
/*
@@ -175,7 +174,7 @@ static void driver_deferred_probe_trigger(void)
* Kick the re-probe thread. It may already be scheduled, but it is
* safe to kick it again.
*/
- queue_work(deferred_wq, &deferred_probe_work);
+ schedule_work(&deferred_probe_work);
}
/**
@@ -211,14 +210,10 @@ void device_unblock_probing(void)
*/
static int deferred_probe_initcall(void)
{
- deferred_wq = create_singlethread_workqueue("deferwq");
- if (WARN_ON(!deferred_wq))
- return -ENOMEM;
-
driver_deferred_probe_enable = true;
driver_deferred_probe_trigger();
/* Sort as many dependencies as possible before exiting initcalls */
- flush_workqueue(deferred_wq);
+ flush_work(&deferred_probe_work);
return 0;
}
late_initcall(deferred_probe_initcall);
@@ -329,6 +324,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = -EPROBE_DEFER;
int local_trigger_count = atomic_read(&deferred_trigger_count);
+ bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE);
if (defer_all_probes) {
/*
@@ -346,6 +342,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
+re_probe:
dev->driver = drv;
/* If using pinctrl, bind pins now before probing */
@@ -383,6 +380,25 @@ static int really_probe(struct device *dev, struct device_driver *drv)
goto probe_failed;
}
+ if (test_remove) {
+ test_remove = false;
+
+ if (dev->bus && dev->bus->remove)
+ dev->bus->remove(dev);
+ else if (drv->remove)
+ drv->remove(dev);
+
+ devres_release_all(dev);
+ driver_sysfs_remove(dev);
+ dev->driver = NULL;
+ dev_set_drvdata(dev, NULL);
+ if (dev->pm_domain && dev->pm_domain->dismiss)
+ dev->pm_domain->dismiss(dev);
+ pm_runtime_reinit(dev);
+
+ goto re_probe;
+ }
+
pinctrl_init_done(dev);
if (dev->pm_domain && dev->pm_domain->sync)
@@ -460,8 +476,7 @@ int driver_probe_done(void)
void wait_for_device_probe(void)
{
/* wait for the deferred probe workqueue to finish */
- if (driver_deferred_probe_enable)
- flush_workqueue(deferred_wq);
+ flush_work(&deferred_probe_work);
/* wait for the known devices to complete their probing */
wait_event(probe_waitqueue, atomic_read(&probe_count) == 0);
diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c
index bdf28f7..640a7e6 100644
--- a/drivers/base/dma-coherent.c
+++ b/drivers/base/dma-coherent.c
@@ -165,6 +165,7 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size,
int order = get_order(size);
unsigned long flags;
int pageno;
+ int dma_memory_map;
if (!dev)
return 0;
@@ -187,11 +188,12 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size,
*/
*dma_handle = mem->device_base + (pageno << PAGE_SHIFT);
*ret = mem->virt_base + (pageno << PAGE_SHIFT);
- if (mem->flags & DMA_MEMORY_MAP)
+ dma_memory_map = (mem->flags & DMA_MEMORY_MAP);
+ spin_unlock_irqrestore(&mem->spinlock, flags);
+ if (dma_memory_map)
memset(*ret, 0, size);
else
memset_io(*ret, 0, size);
- spin_unlock_irqrestore(&mem->spinlock, flags);
return 1;
@@ -261,8 +263,8 @@ int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma,
(mem->virt_base + (mem->size << PAGE_SHIFT))) {
unsigned long off = vma->vm_pgoff;
int start = (vaddr - mem->virt_base) >> PAGE_SHIFT;
- int user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
- int count = size >> PAGE_SHIFT;
+ int user_count = vma_pages(vma);
+ int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
*ret = -ENXIO;
if (off < count && user_count <= count - off) {
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index d799662..8f8b68c 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -198,10 +198,13 @@ int dmam_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr,
rc = dma_declare_coherent_memory(dev, phys_addr, device_addr, size,
flags);
- if (rc == 0)
+ if (rc) {
devres_add(dev, res);
- else
+ rc = 0;
+ } else {
devres_free(res);
+ rc = -ENOMEM;
+ }
return rc;
}
@@ -247,7 +250,7 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
{
int ret = -ENXIO;
#if defined(CONFIG_MMU) && !defined(CONFIG_ARCH_NO_COHERENT_DMA_MMAP)
- unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ unsigned long user_count = vma_pages(vma);
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr));
unsigned long off = vma->vm_pgoff;
@@ -334,7 +337,7 @@ void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags)
return;
}
- unmap_kernel_range((unsigned long)cpu_addr, size);
+ unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size));
vunmap(cpu_addr);
}
#endif
diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index 279e539..be6a599 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -142,13 +142,12 @@ static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq,
}
for (i = 0; i < nvec; i++) {
- desc = alloc_msi_entry(dev);
+ desc = alloc_msi_entry(dev, 1, NULL);
if (!desc)
break;
desc->platform.msi_priv_data = data;
desc->platform.msi_index = base + i;
- desc->nvec_used = 1;
desc->irq = virq ? virq + i : 0;
list_add_tail(&desc->list, dev_to_msi_list(dev));
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 6482d47..c4af003 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -97,7 +97,7 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
int ret;
ret = of_irq_get(dev->dev.of_node, num);
- if (ret >= 0 || ret == -EPROBE_DEFER)
+ if (ret > 0 || ret == -EPROBE_DEFER)
return ret;
}
@@ -108,9 +108,14 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
* IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER*
* settings.
*/
- if (r && r->flags & IORESOURCE_BITS)
- irqd_set_trigger_type(irq_get_irq_data(r->start),
- r->flags & IORESOURCE_BITS);
+ if (r && r->flags & IORESOURCE_BITS) {
+ struct irq_data *irqd;
+
+ irqd = irq_get_irq_data(r->start);
+ if (!irqd)
+ return -ENXIO;
+ irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS);
+ }
return r ? r->start : -ENXIO;
#endif
@@ -175,7 +180,7 @@ int platform_get_irq_byname(struct platform_device *dev, const char *name)
int ret;
ret = of_irq_get_byname(dev->dev.of_node, name);
- if (ret >= 0 || ret == -EPROBE_DEFER)
+ if (ret > 0 || ret == -EPROBE_DEFER)
return ret;
}
@@ -434,6 +439,7 @@ void platform_device_del(struct platform_device *pdev)
int i;
if (pdev) {
+ device_remove_properties(&pdev->dev);
device_del(&pdev->dev);
if (pdev->id_auto) {
@@ -446,8 +452,6 @@ void platform_device_del(struct platform_device *pdev)
if (r->parent)
release_resource(r);
}
-
- device_remove_properties(&pdev->dev);
}
}
EXPORT_SYMBOL_GPL(platform_device_del);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index a1f2aff..e023066 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -45,7 +45,7 @@ static DEFINE_MUTEX(gpd_list_lock);
* and checks that the PM domain pointer is a real generic PM domain.
* Any failure results in NULL being returned.
*/
-struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev)
+static struct generic_pm_domain *genpd_lookup_dev(struct device *dev)
{
struct generic_pm_domain *genpd = NULL, *gpd;
@@ -586,7 +586,7 @@ static int __init genpd_poweroff_unused(void)
}
late_initcall(genpd_poweroff_unused);
-#ifdef CONFIG_PM_SLEEP
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF)
/**
* pm_genpd_present - Check if the given PM domain has been initialized.
@@ -606,6 +606,10 @@ static bool pm_genpd_present(const struct generic_pm_domain *genpd)
return false;
}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+
static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
struct device *dev)
{
@@ -613,9 +617,8 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
}
/**
- * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
+ * genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
* @genpd: PM domain to power off, if possible.
- * @timed: True if latency measurements are allowed.
*
* Check if the given PM domain can be powered off (during system suspend or
* hibernation) and do that if so. Also, in that case propagate to its masters.
@@ -625,8 +628,7 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
* executed sequentially, so it is guaranteed that it will never run twice in
* parallel).
*/
-static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd,
- bool timed)
+static void genpd_sync_poweroff(struct generic_pm_domain *genpd)
{
struct gpd_link *link;
@@ -639,28 +641,26 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd,
/* Choose the deepest state when suspending */
genpd->state_idx = genpd->state_count - 1;
- genpd_power_off(genpd, timed);
+ genpd_power_off(genpd, false);
genpd->status = GPD_STATE_POWER_OFF;
list_for_each_entry(link, &genpd->slave_links, slave_node) {
genpd_sd_counter_dec(link->master);
- pm_genpd_sync_poweroff(link->master, timed);
+ genpd_sync_poweroff(link->master);
}
}
/**
- * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters.
+ * genpd_sync_poweron - Synchronously power on a PM domain and its masters.
* @genpd: PM domain to power on.
- * @timed: True if latency measurements are allowed.
*
* This function is only called in "noirq" and "syscore" stages of system power
* transitions, so it need not acquire locks (all of the "noirq" callbacks are
* executed sequentially, so it is guaranteed that it will never run twice in
* parallel).
*/
-static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd,
- bool timed)
+static void genpd_sync_poweron(struct generic_pm_domain *genpd)
{
struct gpd_link *link;
@@ -668,11 +668,11 @@ static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd,
return;
list_for_each_entry(link, &genpd->slave_links, slave_node) {
- pm_genpd_sync_poweron(link->master, timed);
+ genpd_sync_poweron(link->master);
genpd_sd_counter_inc(link->master);
}
- genpd_power_on(genpd, timed);
+ genpd_power_on(genpd, false);
genpd->status = GPD_STATE_ACTIVE;
}
@@ -784,7 +784,7 @@ static int pm_genpd_suspend_noirq(struct device *dev)
* the same PM domain, so it is not necessary to use locking here.
*/
genpd->suspended_count++;
- pm_genpd_sync_poweroff(genpd, true);
+ genpd_sync_poweroff(genpd);
return 0;
}
@@ -814,7 +814,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
* guaranteed that this function will never run twice in parallel for
* the same PM domain, so it is not necessary to use locking here.
*/
- pm_genpd_sync_poweron(genpd, true);
+ genpd_sync_poweron(genpd);
genpd->suspended_count--;
if (genpd->dev_ops.stop && genpd->dev_ops.start)
@@ -902,12 +902,12 @@ static int pm_genpd_restore_noirq(struct device *dev)
if (genpd->suspended_count++ == 0)
/*
* The boot kernel might put the domain into arbitrary state,
- * so make it appear as powered off to pm_genpd_sync_poweron(),
+ * so make it appear as powered off to genpd_sync_poweron(),
* so that it tries to power it on in case it was really off.
*/
genpd->status = GPD_STATE_POWER_OFF;
- pm_genpd_sync_poweron(genpd, true);
+ genpd_sync_poweron(genpd);
if (genpd->dev_ops.stop && genpd->dev_ops.start)
ret = pm_runtime_force_resume(dev);
@@ -962,9 +962,9 @@ static void genpd_syscore_switch(struct device *dev, bool suspend)
if (suspend) {
genpd->suspended_count++;
- pm_genpd_sync_poweroff(genpd, false);
+ genpd_sync_poweroff(genpd);
} else {
- pm_genpd_sync_poweron(genpd, false);
+ genpd_sync_poweron(genpd);
genpd->suspended_count--;
}
}
@@ -1056,14 +1056,8 @@ static void genpd_free_dev_data(struct device *dev,
dev_pm_put_subsys_data(dev);
}
-/**
- * __pm_genpd_add_device - Add a device to an I/O PM domain.
- * @genpd: PM domain to add the device to.
- * @dev: Device to be added.
- * @td: Set of PM QoS timing parameters to attach to the device.
- */
-int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
- struct gpd_timing_data *td)
+static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
+ struct gpd_timing_data *td)
{
struct generic_pm_domain_data *gpd_data;
int ret = 0;
@@ -1103,15 +1097,28 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
return ret;
}
-EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
/**
- * pm_genpd_remove_device - Remove a device from an I/O PM domain.
- * @genpd: PM domain to remove the device from.
- * @dev: Device to be removed.
+ * __pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ * @td: Set of PM QoS timing parameters to attach to the device.
*/
-int pm_genpd_remove_device(struct generic_pm_domain *genpd,
- struct device *dev)
+int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
+ struct gpd_timing_data *td)
+{
+ int ret;
+
+ mutex_lock(&gpd_list_lock);
+ ret = genpd_add_device(genpd, dev, td);
+ mutex_unlock(&gpd_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
+
+static int genpd_remove_device(struct generic_pm_domain *genpd,
+ struct device *dev)
{
struct generic_pm_domain_data *gpd_data;
struct pm_domain_data *pdd;
@@ -1119,10 +1126,6 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
dev_dbg(dev, "%s()\n", __func__);
- if (!genpd || genpd != pm_genpd_lookup_dev(dev))
- return -EINVAL;
-
- /* The above validation also means we have existing domain_data. */
pdd = dev->power.subsys_data->domain_data;
gpd_data = to_gpd_data(pdd);
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
@@ -1154,15 +1157,24 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
return ret;
}
-EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
/**
- * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
- * @genpd: Master PM domain to add the subdomain to.
- * @subdomain: Subdomain to be added.
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
*/
-int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
- struct generic_pm_domain *subdomain)
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+ struct device *dev)
+{
+ if (!genpd || genpd != genpd_lookup_dev(dev))
+ return -EINVAL;
+
+ return genpd_remove_device(genpd, dev);
+}
+EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
+
+static int genpd_add_subdomain(struct generic_pm_domain *genpd,
+ struct generic_pm_domain *subdomain)
{
struct gpd_link *link, *itr;
int ret = 0;
@@ -1205,6 +1217,23 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
kfree(link);
return ret;
}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+ struct generic_pm_domain *subdomain)
+{
+ int ret;
+
+ mutex_lock(&gpd_list_lock);
+ ret = genpd_add_subdomain(genpd, subdomain);
+ mutex_unlock(&gpd_list_lock);
+
+ return ret;
+}
EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain);
/**
@@ -1278,27 +1307,17 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
genpd->device_count = 0;
genpd->max_off_time_ns = -1;
genpd->max_off_time_changed = true;
+ genpd->provider = NULL;
+ genpd->has_provider = false;
genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
genpd->domain.ops.runtime_resume = genpd_runtime_resume;
genpd->domain.ops.prepare = pm_genpd_prepare;
- genpd->domain.ops.suspend = pm_generic_suspend;
- genpd->domain.ops.suspend_late = pm_generic_suspend_late;
genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
- genpd->domain.ops.resume_early = pm_generic_resume_early;
- genpd->domain.ops.resume = pm_generic_resume;
- genpd->domain.ops.freeze = pm_generic_freeze;
- genpd->domain.ops.freeze_late = pm_generic_freeze_late;
genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
- genpd->domain.ops.thaw_early = pm_generic_thaw_early;
- genpd->domain.ops.thaw = pm_generic_thaw;
- genpd->domain.ops.poweroff = pm_generic_poweroff;
- genpd->domain.ops.poweroff_late = pm_generic_poweroff_late;
genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
- genpd->domain.ops.restore_early = pm_generic_restore_early;
- genpd->domain.ops.restore = pm_generic_restore;
genpd->domain.ops.complete = pm_genpd_complete;
if (genpd->flags & GENPD_FLAG_PM_CLK) {
@@ -1328,7 +1347,71 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
}
EXPORT_SYMBOL_GPL(pm_genpd_init);
+static int genpd_remove(struct generic_pm_domain *genpd)
+{
+ struct gpd_link *l, *link;
+
+ if (IS_ERR_OR_NULL(genpd))
+ return -EINVAL;
+
+ mutex_lock(&genpd->lock);
+
+ if (genpd->has_provider) {
+ mutex_unlock(&genpd->lock);
+ pr_err("Provider present, unable to remove %s\n", genpd->name);
+ return -EBUSY;
+ }
+
+ if (!list_empty(&genpd->master_links) || genpd->device_count) {
+ mutex_unlock(&genpd->lock);
+ pr_err("%s: unable to remove %s\n", __func__, genpd->name);
+ return -EBUSY;
+ }
+
+ list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) {
+ list_del(&link->master_node);
+ list_del(&link->slave_node);
+ kfree(link);
+ }
+
+ list_del(&genpd->gpd_list_node);
+ mutex_unlock(&genpd->lock);
+ cancel_work_sync(&genpd->power_off_work);
+ pr_debug("%s: removed %s\n", __func__, genpd->name);
+
+ return 0;
+}
+
+/**
+ * pm_genpd_remove - Remove a generic I/O PM domain
+ * @genpd: Pointer to PM domain that is to be removed.
+ *
+ * To remove the PM domain, this function:
+ * - Removes the PM domain as a subdomain to any parent domains,
+ * if it was added.
+ * - Removes the PM domain from the list of registered PM domains.
+ *
+ * The PM domain will only be removed, if the associated provider has
+ * been removed, it is not a parent to any other PM domain and has no
+ * devices associated with it.
+ */
+int pm_genpd_remove(struct generic_pm_domain *genpd)
+{
+ int ret;
+
+ mutex_lock(&gpd_list_lock);
+ ret = genpd_remove(genpd);
+ mutex_unlock(&gpd_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pm_genpd_remove);
+
#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+
+typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
+ void *data);
+
/*
* Device Tree based PM domain providers.
*
@@ -1340,8 +1423,8 @@ EXPORT_SYMBOL_GPL(pm_genpd_init);
* maps a PM domain specifier retrieved from the device tree to a PM domain.
*
* Two simple mapping functions have been provided for convenience:
- * - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
- * - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by
+ * - genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
+ * - genpd_xlate_onecell() for mapping of multiple PM domains per node by
* index.
*/
@@ -1366,7 +1449,7 @@ static LIST_HEAD(of_genpd_providers);
static DEFINE_MUTEX(of_genpd_mutex);
/**
- * __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping
+ * genpd_xlate_simple() - Xlate function for direct node-domain mapping
* @genpdspec: OF phandle args to map into a PM domain
* @data: xlate function private data - pointer to struct generic_pm_domain
*
@@ -1374,7 +1457,7 @@ static DEFINE_MUTEX(of_genpd_mutex);
* have their own device tree nodes. The private data of xlate function needs
* to be a valid pointer to struct generic_pm_domain.
*/
-struct generic_pm_domain *__of_genpd_xlate_simple(
+static struct generic_pm_domain *genpd_xlate_simple(
struct of_phandle_args *genpdspec,
void *data)
{
@@ -1382,10 +1465,9 @@ struct generic_pm_domain *__of_genpd_xlate_simple(
return ERR_PTR(-EINVAL);
return data;
}
-EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple);
/**
- * __of_genpd_xlate_onecell() - Xlate function using a single index.
+ * 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
*
@@ -1394,7 +1476,7 @@ EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple);
* 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.
*/
-struct generic_pm_domain *__of_genpd_xlate_onecell(
+static struct generic_pm_domain *genpd_xlate_onecell(
struct of_phandle_args *genpdspec,
void *data)
{
@@ -1414,16 +1496,15 @@ struct generic_pm_domain *__of_genpd_xlate_onecell(
return genpd_data->domains[idx];
}
-EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell);
/**
- * __of_genpd_add_provider() - Register a PM domain provider for a node
+ * 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.
* @data: Context pointer for @xlate callback.
*/
-int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
- void *data)
+static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
+ void *data)
{
struct of_genpd_provider *cp;
@@ -1442,7 +1523,83 @@ int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
return 0;
}
-EXPORT_SYMBOL_GPL(__of_genpd_add_provider);
+
+/**
+ * of_genpd_add_provider_simple() - Register a simple PM domain provider
+ * @np: Device node pointer associated with the PM domain provider.
+ * @genpd: Pointer to PM domain associated with the PM domain provider.
+ */
+int of_genpd_add_provider_simple(struct device_node *np,
+ struct generic_pm_domain *genpd)
+{
+ int ret = -EINVAL;
+
+ if (!np || !genpd)
+ return -EINVAL;
+
+ mutex_lock(&gpd_list_lock);
+
+ if (pm_genpd_present(genpd))
+ ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
+
+ if (!ret) {
+ genpd->provider = &np->fwnode;
+ genpd->has_provider = true;
+ }
+
+ mutex_unlock(&gpd_list_lock);
+
+ return ret;
+}
+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)
+{
+ unsigned int i;
+ int ret = -EINVAL;
+
+ if (!np || !data)
+ return -EINVAL;
+
+ mutex_lock(&gpd_list_lock);
+
+ for (i = 0; i < data->num_domains; i++) {
+ if (!data->domains[i])
+ continue;
+ if (!pm_genpd_present(data->domains[i]))
+ goto error;
+
+ data->domains[i]->provider = &np->fwnode;
+ data->domains[i]->has_provider = true;
+ }
+
+ ret = genpd_add_provider(np, genpd_xlate_onecell, data);
+ if (ret < 0)
+ goto error;
+
+ mutex_unlock(&gpd_list_lock);
+
+ return 0;
+
+error:
+ while (i--) {
+ if (!data->domains[i])
+ continue;
+ data->domains[i]->provider = NULL;
+ data->domains[i]->has_provider = false;
+ }
+
+ mutex_unlock(&gpd_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell);
/**
* of_genpd_del_provider() - Remove a previously registered PM domain provider
@@ -1451,10 +1608,21 @@ EXPORT_SYMBOL_GPL(__of_genpd_add_provider);
void of_genpd_del_provider(struct device_node *np)
{
struct of_genpd_provider *cp;
+ struct generic_pm_domain *gpd;
+ mutex_lock(&gpd_list_lock);
mutex_lock(&of_genpd_mutex);
list_for_each_entry(cp, &of_genpd_providers, link) {
if (cp->node == np) {
+ /*
+ * For each PM domain associated with the
+ * provider, set the 'has_provider' to false
+ * so that the PM domain can be safely removed.
+ */
+ list_for_each_entry(gpd, &gpd_list, gpd_list_node)
+ if (gpd->provider == &np->fwnode)
+ gpd->has_provider = false;
+
list_del(&cp->link);
of_node_put(cp->node);
kfree(cp);
@@ -1462,11 +1630,12 @@ void of_genpd_del_provider(struct device_node *np)
}
}
mutex_unlock(&of_genpd_mutex);
+ mutex_unlock(&gpd_list_lock);
}
EXPORT_SYMBOL_GPL(of_genpd_del_provider);
/**
- * of_genpd_get_from_provider() - Look-up PM domain
+ * genpd_get_from_provider() - Look-up PM domain
* @genpdspec: OF phandle args to use for look-up
*
* Looks for a PM domain provider under the node specified by @genpdspec and if
@@ -1476,7 +1645,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider);
* Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
* on failure.
*/
-struct generic_pm_domain *of_genpd_get_from_provider(
+static struct generic_pm_domain *genpd_get_from_provider(
struct of_phandle_args *genpdspec)
{
struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
@@ -1499,7 +1668,109 @@ struct generic_pm_domain *of_genpd_get_from_provider(
return genpd;
}
-EXPORT_SYMBOL_GPL(of_genpd_get_from_provider);
+
+/**
+ * of_genpd_add_device() - Add a device to an I/O PM domain
+ * @genpdspec: OF phandle args to use for look-up PM domain
+ * @dev: Device to be added.
+ *
+ * Looks-up an I/O PM domain based upon phandle args provided and adds
+ * the device to the PM domain. Returns a negative error code on failure.
+ */
+int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev)
+{
+ struct generic_pm_domain *genpd;
+ int ret;
+
+ mutex_lock(&gpd_list_lock);
+
+ genpd = genpd_get_from_provider(genpdspec);
+ if (IS_ERR(genpd)) {
+ ret = PTR_ERR(genpd);
+ goto out;
+ }
+
+ ret = genpd_add_device(genpd, dev, NULL);
+
+out:
+ mutex_unlock(&gpd_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_genpd_add_device);
+
+/**
+ * of_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @parent_spec: OF phandle args to use for parent PM domain look-up
+ * @subdomain_spec: OF phandle args to use for subdomain look-up
+ *
+ * Looks-up a parent PM domain and subdomain based upon phandle args
+ * provided and adds the subdomain to the parent PM domain. Returns a
+ * negative error code on failure.
+ */
+int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
+ struct of_phandle_args *subdomain_spec)
+{
+ struct generic_pm_domain *parent, *subdomain;
+ int ret;
+
+ mutex_lock(&gpd_list_lock);
+
+ parent = genpd_get_from_provider(parent_spec);
+ if (IS_ERR(parent)) {
+ ret = PTR_ERR(parent);
+ goto out;
+ }
+
+ subdomain = genpd_get_from_provider(subdomain_spec);
+ if (IS_ERR(subdomain)) {
+ ret = PTR_ERR(subdomain);
+ goto out;
+ }
+
+ ret = genpd_add_subdomain(parent, subdomain);
+
+out:
+ mutex_unlock(&gpd_list_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
+
+/**
+ * of_genpd_remove_last - Remove the last PM domain registered for a provider
+ * @provider: Pointer to device structure associated with provider
+ *
+ * Find the last PM domain that was added by a particular provider and
+ * remove this PM domain from the list of PM domains. The provider is
+ * identified by the 'provider' device structure that is passed. The PM
+ * domain will only be removed, if the provider associated with domain
+ * has been removed.
+ *
+ * Returns a valid pointer to struct generic_pm_domain on success or
+ * ERR_PTR() on failure.
+ */
+struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
+{
+ struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT);
+ int ret;
+
+ if (IS_ERR_OR_NULL(np))
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&gpd_list_lock);
+ list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
+ if (gpd->provider == &np->fwnode) {
+ ret = genpd_remove(gpd);
+ genpd = ret ? ERR_PTR(ret) : gpd;
+ break;
+ }
+ }
+ mutex_unlock(&gpd_list_lock);
+
+ return genpd;
+}
+EXPORT_SYMBOL_GPL(of_genpd_remove_last);
/**
* genpd_dev_pm_detach - Detach a device from its PM domain.
@@ -1515,14 +1786,14 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
unsigned int i;
int ret = 0;
- pd = pm_genpd_lookup_dev(dev);
- if (!pd)
+ pd = dev_to_genpd(dev);
+ if (IS_ERR(pd))
return;
dev_dbg(dev, "removing from PM domain %s\n", pd->name);
for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
- ret = pm_genpd_remove_device(pd, dev);
+ ret = genpd_remove_device(pd, dev);
if (ret != -EAGAIN)
break;
@@ -1596,9 +1867,11 @@ int genpd_dev_pm_attach(struct device *dev)
return -ENOENT;
}
- pd = of_genpd_get_from_provider(&pd_args);
+ mutex_lock(&gpd_list_lock);
+ pd = genpd_get_from_provider(&pd_args);
of_node_put(pd_args.np);
if (IS_ERR(pd)) {
+ mutex_unlock(&gpd_list_lock);
dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
__func__, PTR_ERR(pd));
return -EPROBE_DEFER;
@@ -1607,13 +1880,14 @@ int genpd_dev_pm_attach(struct device *dev)
dev_dbg(dev, "adding to PM domain %s\n", pd->name);
for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
- ret = pm_genpd_add_device(pd, dev);
+ ret = genpd_add_device(pd, dev, NULL);
if (ret != -EAGAIN)
break;
mdelay(i);
cond_resched();
}
+ mutex_unlock(&gpd_list_lock);
if (ret < 0) {
dev_err(dev, "failed to add to PM domain %s: %d",
@@ -1636,7 +1910,7 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
/*** debugfs support ***/
-#ifdef CONFIG_PM_ADVANCED_DEBUG
+#ifdef CONFIG_DEBUG_FS
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/debugfs.h>
@@ -1784,4 +2058,4 @@ static void __exit pm_genpd_debug_exit(void)
debugfs_remove_recursive(pm_genpd_debugfs_dir);
}
__exitcall(pm_genpd_debug_exit);
-#endif /* CONFIG_PM_ADVANCED_DEBUG */
+#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index df0c709..4c7c6da 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -584,7 +584,6 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
struct clk *clk;
unsigned long freq, old_freq;
unsigned long u_volt, u_volt_min, u_volt_max;
- unsigned long ou_volt, ou_volt_min, ou_volt_max;
int ret;
if (unlikely(!target_freq)) {
@@ -620,11 +619,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
}
old_opp = _find_freq_ceil(opp_table, &old_freq);
- if (!IS_ERR(old_opp)) {
- ou_volt = old_opp->u_volt;
- ou_volt_min = old_opp->u_volt_min;
- ou_volt_max = old_opp->u_volt_max;
- } else {
+ if (IS_ERR(old_opp)) {
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
__func__, old_freq, PTR_ERR(old_opp));
}
@@ -683,7 +678,8 @@ restore_freq:
restore_voltage:
/* This shouldn't harm even if the voltages weren't updated earlier */
if (!IS_ERR(old_opp))
- _set_opp_voltage(dev, reg, ou_volt, ou_volt_min, ou_volt_max);
+ _set_opp_voltage(dev, reg, old_opp->u_volt,
+ old_opp->u_volt_min, old_opp->u_volt_max);
return ret;
}
diff --git a/drivers/base/power/opp/of.c b/drivers/base/power/opp/of.c
index 1dfd3dd..5552211 100644
--- a/drivers/base/power/opp/of.c
+++ b/drivers/base/power/opp/of.c
@@ -71,8 +71,18 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
u32 version;
int ret;
- if (!opp_table->supported_hw)
- return true;
+ if (!opp_table->supported_hw) {
+ /*
+ * In the case that no supported_hw has been set by the
+ * platform but there is an opp-supported-hw value set for
+ * an OPP then the OPP should not be enabled as there is
+ * no way to see if the hardware supports it.
+ */
+ if (of_find_property(np, "opp-supported-hw", NULL))
+ return false;
+ else
+ return true;
+ }
while (count--) {
ret = of_property_read_u32_index(np, "opp-supported-hw", count,
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index a038033..2a4435d 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -105,8 +105,8 @@ struct regmap {
bool defer_caching;
- u8 read_flag_mask;
- u8 write_flag_mask;
+ unsigned long read_flag_mask;
+ unsigned long write_flag_mask;
/* number of bits to (left) shift the reg value when formatting*/
int reg_shift;
@@ -173,6 +173,7 @@ struct regcache_ops {
int (*drop)(struct regmap *map, unsigned int min, unsigned int max);
};
+bool regmap_cached(struct regmap *map, unsigned int reg);
bool regmap_writeable(struct regmap *map, unsigned int reg);
bool regmap_readable(struct regmap *map, unsigned int reg);
bool regmap_volatile(struct regmap *map, unsigned int reg);
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 1ee3d40..36ce351 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -77,6 +77,17 @@ static void regmap_debugfs_free_dump_cache(struct regmap *map)
}
}
+static bool regmap_printable(struct regmap *map, unsigned int reg)
+{
+ if (regmap_precious(map, reg))
+ return false;
+
+ if (!regmap_readable(map, reg) && !regmap_cached(map, reg))
+ return false;
+
+ return true;
+}
+
/*
* Work out where the start offset maps into register numbers, bearing
* in mind that we suppress hidden registers.
@@ -105,8 +116,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map,
if (list_empty(&map->debugfs_off_cache)) {
for (; i <= map->max_register; i += map->reg_stride) {
/* Skip unprinted registers, closing off cache entry */
- if (!regmap_readable(map, i) ||
- regmap_precious(map, i)) {
+ if (!regmap_printable(map, i)) {
if (c) {
c->max = p - 1;
c->max_reg = i - map->reg_stride;
@@ -204,7 +214,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from,
start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p);
for (i = start_reg; i <= to; i += map->reg_stride) {
- if (!regmap_readable(map, i))
+ if (!regmap_readable(map, i) && !regmap_cached(map, i))
continue;
if (regmap_precious(map, i))
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index e964d06..ae63bb0 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -93,6 +93,29 @@ bool regmap_writeable(struct regmap *map, unsigned int reg)
return true;
}
+bool regmap_cached(struct regmap *map, unsigned int reg)
+{
+ int ret;
+ unsigned int val;
+
+ if (map->cache == REGCACHE_NONE)
+ return false;
+
+ if (!map->cache_ops)
+ return false;
+
+ if (map->max_register && reg > map->max_register)
+ return false;
+
+ map->lock(map->lock_arg);
+ ret = regcache_read(map, reg, &val);
+ map->unlock(map->lock_arg);
+ if (ret)
+ return false;
+
+ return true;
+}
+
bool regmap_readable(struct regmap *map, unsigned int reg)
{
if (!map->reg_read)
@@ -749,6 +772,9 @@ struct regmap *__regmap_init(struct device *dev,
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;
@@ -768,6 +794,9 @@ struct regmap *__regmap_init(struct device *dev,
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;
@@ -782,6 +811,9 @@ struct regmap *__regmap_init(struct device *dev,
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;
@@ -1296,12 +1328,26 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
return 0;
}
+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;
+}
+
int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len)
{
struct regmap_range_node *range;
unsigned long flags;
- u8 *u8 = map->work_buf;
void *work_val = map->work_buf + map->format.reg_bytes +
map->format.pad_bytes;
void *buf;
@@ -1370,8 +1416,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
}
map->format.format_reg(map->work_buf, reg, map->reg_shift);
-
- u8[0] |= map->write_flag_mask;
+ 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
@@ -2251,7 +2297,6 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
unsigned int val_len)
{
struct regmap_range_node *range;
- u8 *u8 = map->work_buf;
int ret;
WARN_ON(!map->bus);
@@ -2268,15 +2313,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
}
map->format.format_reg(map->work_buf, reg, map->reg_shift);
-
- /*
- * Some buses or devices flag reads by setting the high bits in the
- * register address; since it's always the high bits for all
- * current formats we can do this here rather than in
- * formatting. This may break if we get interesting formats.
- */
- u8[0] |= map->read_flag_mask;
-
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+ map->read_flag_mask);
trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes);
ret = map->bus->read(map->bus_context, map->work_buf,
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index 75b98aa..b63f23e 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -6,7 +6,6 @@
*/
#include <linux/sysfs.h>
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/slab.h>
@@ -160,11 +159,3 @@ static int __init soc_bus_register(void)
return bus_register(&soc_bus_type);
}
core_initcall(soc_bus_register);
-
-static void __exit soc_bus_unregister(void)
-{
- ida_destroy(&soc_ida);
-
- bus_unregister(&soc_bus_type);
-}
-module_exit(soc_bus_unregister);
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
index 5b0ef7b..5ce6d41 100644
--- a/drivers/bluetooth/bcm203x.c
+++ b/drivers/bluetooth/bcm203x.c
@@ -185,10 +185,8 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id
data->state = BCM203X_LOAD_MINIDRV;
data->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!data->urb) {
- BT_ERR("Can't allocate URB");
+ if (!data->urb)
return -ENOMEM;
- }
if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
BT_ERR("Mini driver request failed");
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 3ff229b..c4a75a1 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -377,21 +377,7 @@ static struct miscdevice vhci_miscdev = {
.fops = &vhci_fops,
.minor = VHCI_MINOR,
};
-
-static int __init vhci_init(void)
-{
- BT_INFO("Virtual HCI driver ver %s", VERSION);
-
- return misc_register(&vhci_miscdev);
-}
-
-static void __exit vhci_exit(void)
-{
- misc_deregister(&vhci_miscdev);
-}
-
-module_init(vhci_init);
-module_exit(vhci_exit);
+module_misc_device(vhci_miscdev);
module_param(amp, bool, 0644);
MODULE_PARM_DESC(amp, "Create AMP controller device");
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
index ffa7c9d..8900823 100644
--- a/drivers/bus/arm-cci.c
+++ b/drivers/bus/arm-cci.c
@@ -144,15 +144,12 @@ struct cci_pmu {
int num_cntrs;
atomic_t active_events;
struct mutex reserve_mutex;
- struct list_head entry;
+ struct hlist_node node;
cpumask_t cpus;
};
#define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu))
-static DEFINE_MUTEX(cci_pmu_mutex);
-static LIST_HEAD(cci_pmu_list);
-
enum cci_models {
#ifdef CONFIG_ARM_CCI400_PMU
CCI400_R0,
@@ -1506,25 +1503,21 @@ static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev)
return perf_pmu_register(&cci_pmu->pmu, name, -1);
}
-static int cci_pmu_offline_cpu(unsigned int cpu)
+static int cci_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
{
- struct cci_pmu *cci_pmu;
+ struct cci_pmu *cci_pmu = hlist_entry_safe(node, struct cci_pmu, node);
unsigned int target;
- mutex_lock(&cci_pmu_mutex);
- list_for_each_entry(cci_pmu, &cci_pmu_list, entry) {
- if (!cpumask_test_and_clear_cpu(cpu, &cci_pmu->cpus))
- continue;
- target = cpumask_any_but(cpu_online_mask, cpu);
- if (target >= nr_cpu_ids)
- continue;
- /*
- * TODO: migrate context once core races on event->ctx have
- * been fixed.
- */
- cpumask_set_cpu(target, &cci_pmu->cpus);
- }
- mutex_unlock(&cci_pmu_mutex);
+ if (!cpumask_test_and_clear_cpu(cpu, &cci_pmu->cpus))
+ return 0;
+ target = cpumask_any_but(cpu_online_mask, cpu);
+ if (target >= nr_cpu_ids)
+ return 0;
+ /*
+ * TODO: migrate context once core races on event->ctx have
+ * been fixed.
+ */
+ cpumask_set_cpu(target, &cci_pmu->cpus);
return 0;
}
@@ -1768,10 +1761,8 @@ static int cci_pmu_probe(struct platform_device *pdev)
if (ret)
return ret;
- mutex_lock(&cci_pmu_mutex);
- list_add(&cci_pmu->entry, &cci_pmu_list);
- mutex_unlock(&cci_pmu_mutex);
-
+ cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CCI_ONLINE,
+ &cci_pmu->node);
pr_info("ARM %s PMU driver probed", cci_pmu->model->name);
return 0;
}
@@ -1804,9 +1795,9 @@ static int __init cci_platform_init(void)
{
int ret;
- ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_CCI_ONLINE,
- "AP_PERF_ARM_CCI_ONLINE", NULL,
- cci_pmu_offline_cpu);
+ ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CCI_ONLINE,
+ "AP_PERF_ARM_CCI_ONLINE", NULL,
+ cci_pmu_offline_cpu);
if (ret)
return ret;
diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index 884c030..d1074d9 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -167,7 +167,7 @@ struct arm_ccn_dt {
struct hrtimer hrtimer;
cpumask_t cpu;
- struct list_head entry;
+ struct hlist_node node;
struct pmu pmu;
};
@@ -190,9 +190,6 @@ struct arm_ccn {
int mn_id;
};
-static DEFINE_MUTEX(arm_ccn_mutex);
-static LIST_HEAD(arm_ccn_list);
-
static int arm_ccn_node_to_xp(int node)
{
return node / CCN_NUM_XP_PORTS;
@@ -1214,30 +1211,24 @@ static enum hrtimer_restart arm_ccn_pmu_timer_handler(struct hrtimer *hrtimer)
}
-static int arm_ccn_pmu_offline_cpu(unsigned int cpu)
+static int arm_ccn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node)
{
- struct arm_ccn_dt *dt;
+ struct arm_ccn_dt *dt = hlist_entry_safe(node, struct arm_ccn_dt, node);
+ struct arm_ccn *ccn = container_of(dt, struct arm_ccn, dt);
unsigned int target;
- mutex_lock(&arm_ccn_mutex);
- list_for_each_entry(dt, &arm_ccn_list, entry) {
- struct arm_ccn *ccn = container_of(dt, struct arm_ccn, dt);
-
- if (!cpumask_test_and_clear_cpu(cpu, &dt->cpu))
- continue;
- target = cpumask_any_but(cpu_online_mask, cpu);
- if (target >= nr_cpu_ids)
- continue;
- perf_pmu_migrate_context(&dt->pmu, cpu, target);
- cpumask_set_cpu(target, &dt->cpu);
- if (ccn->irq)
- WARN_ON(irq_set_affinity_hint(ccn->irq, &dt->cpu) != 0);
- }
- mutex_unlock(&arm_ccn_mutex);
+ if (!cpumask_test_and_clear_cpu(cpu, &dt->cpu))
+ return 0;
+ target = cpumask_any_but(cpu_online_mask, cpu);
+ if (target >= nr_cpu_ids)
+ return 0;
+ perf_pmu_migrate_context(&dt->pmu, cpu, target);
+ cpumask_set_cpu(target, &dt->cpu);
+ if (ccn->irq)
+ WARN_ON(irq_set_affinity_hint(ccn->irq, &dt->cpu) != 0);
return 0;
}
-
static DEFINE_IDA(arm_ccn_pmu_ida);
static int arm_ccn_pmu_init(struct arm_ccn *ccn)
@@ -1321,9 +1312,8 @@ static int arm_ccn_pmu_init(struct arm_ccn *ccn)
if (err)
goto error_pmu_register;
- mutex_lock(&arm_ccn_mutex);
- list_add(&ccn->dt.entry, &arm_ccn_list);
- mutex_unlock(&arm_ccn_mutex);
+ cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
+ &ccn->dt.node);
return 0;
error_pmu_register:
@@ -1339,10 +1329,8 @@ static void arm_ccn_pmu_cleanup(struct arm_ccn *ccn)
{
int i;
- mutex_lock(&arm_ccn_mutex);
- list_del(&ccn->dt.entry);
- mutex_unlock(&arm_ccn_mutex);
-
+ cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
+ &ccn->dt.node);
if (ccn->irq)
irq_set_affinity_hint(ccn->irq, NULL);
for (i = 0; i < ccn->num_xps; i++)
@@ -1573,9 +1561,9 @@ static int __init arm_ccn_init(void)
{
int i, ret;
- ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE,
- "AP_PERF_ARM_CCN_ONLINE", NULL,
- arm_ccn_pmu_offline_cpu);
+ ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_CCN_ONLINE,
+ "AP_PERF_ARM_CCN_ONLINE", NULL,
+ arm_ccn_pmu_offline_cpu);
if (ret)
return ret;
@@ -1587,7 +1575,7 @@ static int __init arm_ccn_init(void)
static void __exit arm_ccn_exit(void)
{
- cpuhp_remove_state_nocalls(CPUHP_AP_PERF_ARM_CCN_ONLINE);
+ cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_CCN_ONLINE);
platform_driver_unregister(&arm_ccn_driver);
}
diff --git a/drivers/bus/mips_cdmm.c b/drivers/bus/mips_cdmm.c
index cad49bc..1b14256 100644
--- a/drivers/bus/mips_cdmm.c
+++ b/drivers/bus/mips_cdmm.c
@@ -596,19 +596,20 @@ BUILD_PERDEV_HELPER(cpu_down) /* int mips_cdmm_cpu_down_helper(...) */
BUILD_PERDEV_HELPER(cpu_up) /* int mips_cdmm_cpu_up_helper(...) */
/**
- * mips_cdmm_bus_down() - Tear down the CDMM bus.
- * @data: Pointer to unsigned int CPU number.
+ * mips_cdmm_cpu_down_prep() - Callback for CPUHP DOWN_PREP:
+ * Tear down the CDMM bus.
+ * @cpu: unsigned int CPU number.
*
* This function is executed on the hotplugged CPU and calls the CDMM
* driver cpu_down callback for all devices on that CPU.
*/
-static long mips_cdmm_bus_down(void *data)
+static int mips_cdmm_cpu_down_prep(unsigned int cpu)
{
struct mips_cdmm_bus *bus;
long ret;
/* Inform all the devices on the bus */
- ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, data,
+ ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
mips_cdmm_cpu_down_helper);
/*
@@ -623,8 +624,8 @@ static long mips_cdmm_bus_down(void *data)
}
/**
- * mips_cdmm_bus_up() - Bring up the CDMM bus.
- * @data: Pointer to unsigned int CPU number.
+ * mips_cdmm_cpu_online() - Callback for CPUHP ONLINE: Bring up the CDMM bus.
+ * @cpu: unsigned int CPU number.
*
* This work_on_cpu callback function is executed on a given CPU to discover
* CDMM devices on that CPU, or to call the CDMM driver cpu_up callback for all
@@ -634,7 +635,7 @@ static long mips_cdmm_bus_down(void *data)
* initialisation. When CPUs are brought online the function is
* invoked directly on the hotplugged CPU.
*/
-static long mips_cdmm_bus_up(void *data)
+static int mips_cdmm_cpu_online(unsigned int cpu)
{
struct mips_cdmm_bus *bus;
long ret;
@@ -651,51 +652,13 @@ static long mips_cdmm_bus_up(void *data)
mips_cdmm_bus_discover(bus);
else
/* Inform all the devices on the bus */
- ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, data,
+ ret = bus_for_each_dev(&mips_cdmm_bustype, NULL, &cpu,
mips_cdmm_cpu_up_helper);
return ret;
}
/**
- * mips_cdmm_cpu_notify() - Take action when a CPU is going online or offline.
- * @nb: CPU notifier block .
- * @action: Event that has taken place (CPU_*).
- * @data: CPU number.
- *
- * This notifier is used to keep the CDMM buses updated as CPUs are offlined and
- * onlined. When CPUs go offline or come back online, so does their CDMM bus, so
- * devices must be informed. Also when CPUs come online for the first time the
- * devices on the CDMM bus need discovering.
- *
- * Returns: NOTIFY_OK if event was used.
- * NOTIFY_DONE if we didn't care.
- */
-static int mips_cdmm_cpu_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- unsigned int cpu = (unsigned int)data;
-
- switch (action & ~CPU_TASKS_FROZEN) {
- case CPU_ONLINE:
- case CPU_DOWN_FAILED:
- mips_cdmm_bus_up(&cpu);
- break;
- case CPU_DOWN_PREPARE:
- mips_cdmm_bus_down(&cpu);
- break;
- default:
- return NOTIFY_DONE;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block mips_cdmm_cpu_nb = {
- .notifier_call = mips_cdmm_cpu_notify,
-};
-
-/**
* mips_cdmm_init() - Initialise CDMM bus.
*
* Initialise CDMM bus, discover CDMM devices for online CPUs, and arrange for
@@ -703,7 +666,6 @@ static struct notifier_block mips_cdmm_cpu_nb = {
*/
static int __init mips_cdmm_init(void)
{
- unsigned int cpu;
int ret;
/* Register the bus */
@@ -712,19 +674,11 @@ static int __init mips_cdmm_init(void)
return ret;
/* We want to be notified about new CPUs */
- ret = register_cpu_notifier(&mips_cdmm_cpu_nb);
- if (ret) {
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "bus/cdmm:online",
+ mips_cdmm_cpu_online, mips_cdmm_cpu_down_prep);
+ if (ret < 0)
pr_warn("cdmm: Failed to register CPU notifier\n");
- goto out;
- }
-
- /* Discover devices on CDMM of online CPUs */
- for_each_online_cpu(cpu)
- work_on_cpu(cpu, mips_cdmm_bus_up, &cpu);
- return 0;
-out:
- bus_unregister(&mips_cdmm_bustype);
return ret;
}
subsys_initcall(mips_cdmm_init);
diff --git a/drivers/char/bfin-otp.c b/drivers/char/bfin-otp.c
index 44660f1..35d46da 100644
--- a/drivers/char/bfin-otp.c
+++ b/drivers/char/bfin-otp.c
@@ -230,45 +230,7 @@ static struct miscdevice bfin_otp_misc_device = {
.name = DRIVER_NAME,
.fops = &bfin_otp_fops,
};
-
-/**
- * bfin_otp_init - Initialize module
- *
- * Registers the device and notifier handler. Actual device
- * initialization is handled by bfin_otp_open().
- */
-static int __init bfin_otp_init(void)
-{
- int ret;
-
- stampit();
-
- ret = misc_register(&bfin_otp_misc_device);
- if (ret) {
- pr_init(KERN_ERR PFX "unable to register a misc device\n");
- return ret;
- }
-
- pr_init(KERN_INFO PFX "initialized\n");
-
- return 0;
-}
-
-/**
- * bfin_otp_exit - Deinitialize module
- *
- * Unregisters the device and notifier handler. Actual device
- * deinitialization is handled by bfin_otp_close().
- */
-static void __exit bfin_otp_exit(void)
-{
- stampit();
-
- misc_deregister(&bfin_otp_misc_device);
-}
-
-module_init(bfin_otp_init);
-module_exit(bfin_otp_exit);
+module_misc_device(bfin_otp_misc_device);
MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
MODULE_DESCRIPTION("Blackfin OTP Memory Interface");
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index a33163d..5bb1985 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -381,6 +381,9 @@ static ssize_t read_kmem(struct file *file, char __user *buf,
char *kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
int err = 0;
+ if (!pfn_valid(PFN_DOWN(p)))
+ return -EIO;
+
read = 0;
if (p < (unsigned long) high_memory) {
low_count = count;
@@ -509,6 +512,9 @@ static ssize_t write_kmem(struct file *file, const char __user *buf,
char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
int err = 0;
+ if (!pfn_valid(PFN_DOWN(p)))
+ return -EIO;
+
if (p < (unsigned long) high_memory) {
unsigned long to_write = min_t(unsigned long, count,
(unsigned long)high_memory - p);
diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c
index 2874004..972c40a 100644
--- a/drivers/char/mwave/3780i.c
+++ b/drivers/char/mwave/3780i.c
@@ -124,7 +124,7 @@ static void dsp3780I_WriteGenCfg(unsigned short usDspBaseIO, unsigned uIndex,
MKBYTE(rSlaveControl));
rSlaveControl_Save = rSlaveControl;
- rSlaveControl.ConfigMode = TRUE;
+ rSlaveControl.ConfigMode = true;
PRINTK_2(TRACE_3780I,
"3780i::dsp3780i_WriteGenCfg entry rSlaveControl+ConfigMode %x\n",
@@ -155,7 +155,7 @@ unsigned char dsp3780I_ReadGenCfg(unsigned short usDspBaseIO,
MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl);
rSlaveControl_Save = rSlaveControl;
- rSlaveControl.ConfigMode = TRUE;
+ rSlaveControl.ConfigMode = true;
OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl));
OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex);
ucValue = InByteDsp(DSP_ConfigData);
@@ -230,7 +230,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
rUartCfg1.BaseIO = 3;
break;
}
- rUartCfg2.Enable = TRUE;
+ rUartCfg2.Enable = true;
}
rHBridgeCfg1.Reserved = rHBridgeCfg2.Reserved = 0;
@@ -238,7 +238,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
rHBridgeCfg1.IrqPulse = pSettings->bDspIrqPulse;
rHBridgeCfg1.Irq = (unsigned char) pIrqMap[pSettings->usDspIrq];
rHBridgeCfg1.AccessMode = 1;
- rHBridgeCfg2.Enable = TRUE;
+ rHBridgeCfg2.Enable = true;
rBusmasterCfg2.Reserved = 0;
@@ -278,8 +278,8 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
* soft-reset active for 10ms.
*/
rSlaveControl.ClockControl = 0;
- rSlaveControl.SoftReset = TRUE;
- rSlaveControl.ConfigMode = FALSE;
+ rSlaveControl.SoftReset = true;
+ rSlaveControl.ConfigMode = false;
rSlaveControl.Reserved = 0;
PRINTK_4(TRACE_3780I,
@@ -302,7 +302,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
for (i = 0; i < 11; i++)
udelay(2000);
- rSlaveControl.SoftReset = FALSE;
+ rSlaveControl.SoftReset = false;
OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl);
@@ -326,10 +326,10 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
}
- rHBridgeControl.EnableDspInt = FALSE;
- rHBridgeControl.MemAutoInc = TRUE;
- rHBridgeControl.IoAutoInc = FALSE;
- rHBridgeControl.DiagnosticMode = FALSE;
+ rHBridgeControl.EnableDspInt = false;
+ rHBridgeControl.MemAutoInc = true;
+ rHBridgeControl.IoAutoInc = false;
+ rHBridgeControl.DiagnosticMode = false;
PRINTK_3(TRACE_3780I,
"3780i::dsp3780i_EnableDSP DSP_HBridgeControl %x rHBridgeControl %x\n",
@@ -345,7 +345,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings,
ChipID = ReadMsaCfg(DSP_ChipID);
PRINTK_2(TRACE_3780I,
- "3780i::dsp3780I_EnableDSP exiting bRC=TRUE, ChipID %x\n",
+ "3780i::dsp3780I_EnableDSP exiting bRC=true, ChipID %x\n",
ChipID);
return 0;
@@ -361,8 +361,8 @@ int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings)
PRINTK_1(TRACE_3780I, "3780i::dsp3780i_DisableDSP entry\n");
rSlaveControl.ClockControl = 0;
- rSlaveControl.SoftReset = TRUE;
- rSlaveControl.ConfigMode = FALSE;
+ rSlaveControl.SoftReset = true;
+ rSlaveControl.ConfigMode = false;
rSlaveControl.Reserved = 0;
spin_lock_irqsave(&dsp_lock, flags);
OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl));
@@ -398,14 +398,14 @@ int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings)
PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rHBridgeControl %x\n",
MKWORD(rHBridgeControl));
- rHBridgeControl.EnableDspInt = FALSE;
+ rHBridgeControl.EnableDspInt = false;
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
spin_unlock_irqrestore(&dsp_lock, flags);
/* Reset the core via the boot domain register */
- rBootDomain.ResetCore = TRUE;
- rBootDomain.Halt = TRUE;
- rBootDomain.NMI = TRUE;
+ rBootDomain.ResetCore = true;
+ rBootDomain.Halt = true;
+ rBootDomain.NMI = true;
rBootDomain.Reserved = 0;
PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Reset rBootDomain %x\n",
@@ -438,26 +438,26 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings)
/* Transition the core to a running state */
- rBootDomain.ResetCore = TRUE;
- rBootDomain.Halt = FALSE;
- rBootDomain.NMI = TRUE;
+ rBootDomain.ResetCore = true;
+ rBootDomain.Halt = false;
+ rBootDomain.NMI = true;
rBootDomain.Reserved = 0;
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
udelay(5);
- rBootDomain.ResetCore = FALSE;
+ rBootDomain.ResetCore = false;
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
udelay(5);
- rBootDomain.NMI = FALSE;
+ rBootDomain.NMI = false;
WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain));
udelay(5);
/* Enable DSP to PC interrupt */
spin_lock_irqsave(&dsp_lock, flags);
MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
- rHBridgeControl.EnableDspInt = TRUE;
+ rHBridgeControl.EnableDspInt = true;
PRINTK_2(TRACE_3780I, "3780i::dsp3780i_Run rHBridgeControl %x\n",
MKWORD(rHBridgeControl));
@@ -466,7 +466,7 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings)
spin_unlock_irqrestore(&dsp_lock, flags);
- PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run exit bRC=TRUE\n");
+ PRINTK_1(TRACE_3780I, "3780i::dsp3780i_Run exit bRC=true\n");
return 0;
}
@@ -508,7 +508,7 @@ int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
PRINTK_1(TRACE_3780I,
- "3780I::dsp3780I_ReadDStore exit bRC=TRUE\n");
+ "3780I::dsp3780I_ReadDStore exit bRC=true\n");
return 0;
}
@@ -550,7 +550,7 @@ int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO,
PRINTK_1(TRACE_3780I,
- "3780I::dsp3780I_ReadAndClearDStore exit bRC=TRUE\n");
+ "3780I::dsp3780I_ReadAndClearDStore exit bRC=true\n");
return 0;
}
@@ -592,7 +592,7 @@ int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer,
PRINTK_1(TRACE_3780I,
- "3780I::dsp3780D_WriteDStore exit bRC=TRUE\n");
+ "3780I::dsp3780D_WriteDStore exit bRC=true\n");
return 0;
}
@@ -640,7 +640,7 @@ int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
}
PRINTK_1(TRACE_3780I,
- "3780I::dsp3780I_ReadIStore exit bRC=TRUE\n");
+ "3780I::dsp3780I_ReadIStore exit bRC=true\n");
return 0;
}
@@ -689,7 +689,7 @@ int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer,
}
PRINTK_1(TRACE_3780I,
- "3780I::dsp3780I_WriteIStore exit bRC=TRUE\n");
+ "3780I::dsp3780I_WriteIStore exit bRC=true\n");
return 0;
}
@@ -713,7 +713,7 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
*/
spin_lock_irqsave(&dsp_lock, flags);
MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl);
- rHBridgeControl.EnableDspInt = FALSE;
+ rHBridgeControl.EnableDspInt = false;
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
*pusIPCSource = InWordDsp(DSP_Interrupt);
@@ -725,7 +725,7 @@ int dsp3780I_GetIPCSource(unsigned short usDspBaseIO,
OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource));
- rHBridgeControl.EnableDspInt = TRUE;
+ rHBridgeControl.EnableDspInt = true;
OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl));
spin_unlock_irqrestore(&dsp_lock, flags);
diff --git a/drivers/char/mwave/3780i.h b/drivers/char/mwave/3780i.h
index fba6ab1..9ccb6b2 100644
--- a/drivers/char/mwave/3780i.h
+++ b/drivers/char/mwave/3780i.h
@@ -101,7 +101,7 @@ typedef struct {
} DSP_UART_CFG_1;
typedef struct {
- unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=FALSE, 1=TRUE */
+ unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=false, 1=true */
unsigned char Reserved:7; /* 0: Reserved */
} DSP_UART_CFG_2;
@@ -114,7 +114,7 @@ typedef struct {
} DSP_HBRIDGE_CFG_1;
typedef struct {
- unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=FALSE, 1=TRUE */
+ unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=false, 1=true */
unsigned char Reserved:7; /* 0: Reserved */
} DSP_HBRIDGE_CFG_2;
@@ -133,12 +133,12 @@ typedef struct {
typedef struct {
- unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=FALSE, 1=TRUE */
+ unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=false, 1=true */
unsigned char Reserved:7; /* 0: Reserved */
} DSP_ISA_PROT_CFG;
typedef struct {
- unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=FALSE, 1=TRUE */
+ unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=false, 1=true */
unsigned char Reserved:7; /* 0: Reserved */
} DSP_POWER_MGMT_CFG;
diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c
index 164544a..3a3ff2e 100644
--- a/drivers/char/mwave/mwavedd.c
+++ b/drivers/char/mwave/mwavedd.c
@@ -296,8 +296,8 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
pDrvData->IPCs[ipcnum].usIntCount);
mutex_lock(&mwave_mutex);
- pDrvData->IPCs[ipcnum].bIsHere = FALSE;
- pDrvData->IPCs[ipcnum].bIsEnabled = TRUE;
+ pDrvData->IPCs[ipcnum].bIsHere = false;
+ pDrvData->IPCs[ipcnum].bIsEnabled = true;
mutex_unlock(&mwave_mutex);
PRINTK_2(TRACE_MWAVE,
@@ -324,7 +324,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
pDrvData->IPCs[ipcnum].usIntCount);
mutex_lock(&mwave_mutex);
- if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) {
+ if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
DECLARE_WAITQUEUE(wait, current);
PRINTK_2(TRACE_MWAVE,
@@ -332,7 +332,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
" ipc %x going to sleep\n",
ipcnum);
add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
- pDrvData->IPCs[ipcnum].bIsHere = TRUE;
+ pDrvData->IPCs[ipcnum].bIsHere = true;
set_current_state(TASK_INTERRUPTIBLE);
/* check whether an event was signalled by */
/* the interrupt handler while we were gone */
@@ -355,7 +355,7 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
" application\n",
ipcnum);
}
- pDrvData->IPCs[ipcnum].bIsHere = FALSE;
+ pDrvData->IPCs[ipcnum].bIsHere = false;
remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
set_current_state(TASK_RUNNING);
PRINTK_2(TRACE_MWAVE,
@@ -384,9 +384,9 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd,
return -EINVAL;
}
mutex_lock(&mwave_mutex);
- if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) {
- pDrvData->IPCs[ipcnum].bIsEnabled = FALSE;
- if (pDrvData->IPCs[ipcnum].bIsHere == TRUE) {
+ if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
+ pDrvData->IPCs[ipcnum].bIsEnabled = false;
+ if (pDrvData->IPCs[ipcnum].bIsHere == true) {
wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue);
}
}
@@ -541,7 +541,7 @@ static void mwave_exit(void)
if (pDrvData->device_registered) {
device_unregister(&mwave_device);
- pDrvData->device_registered = FALSE;
+ pDrvData->device_registered = false;
}
#endif
@@ -576,16 +576,16 @@ static int __init mwave_init(void)
memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA));
- pDrvData->bBDInitialized = FALSE;
- pDrvData->bResourcesClaimed = FALSE;
- pDrvData->bDSPEnabled = FALSE;
- pDrvData->bDSPReset = FALSE;
- pDrvData->bMwaveDevRegistered = FALSE;
+ pDrvData->bBDInitialized = false;
+ pDrvData->bResourcesClaimed = false;
+ pDrvData->bDSPEnabled = false;
+ pDrvData->bDSPReset = false;
+ pDrvData->bMwaveDevRegistered = false;
pDrvData->sLine = -1;
for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) {
- pDrvData->IPCs[i].bIsEnabled = FALSE;
- pDrvData->IPCs[i].bIsHere = FALSE;
+ pDrvData->IPCs[i].bIsEnabled = false;
+ pDrvData->IPCs[i].bIsHere = false;
pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */
init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue);
}
@@ -601,7 +601,7 @@ static int __init mwave_init(void)
" Failed to initialize board data\n");
goto cleanup_error;
}
- pDrvData->bBDInitialized = TRUE;
+ pDrvData->bBDInitialized = true;
retval = tp3780I_CalcResources(&pDrvData->rBDData);
PRINTK_2(TRACE_MWAVE,
@@ -626,7 +626,7 @@ static int __init mwave_init(void)
" Failed to claim resources\n");
goto cleanup_error;
}
- pDrvData->bResourcesClaimed = TRUE;
+ pDrvData->bResourcesClaimed = true;
retval = tp3780I_EnableDSP(&pDrvData->rBDData);
PRINTK_2(TRACE_MWAVE,
@@ -639,7 +639,7 @@ static int __init mwave_init(void)
" Failed to enable DSP\n");
goto cleanup_error;
}
- pDrvData->bDSPEnabled = TRUE;
+ pDrvData->bDSPEnabled = true;
if (misc_register(&mwave_misc_dev) < 0) {
PRINTK_ERROR(KERN_ERR_MWAVE
@@ -647,7 +647,7 @@ static int __init mwave_init(void)
" Failed to register misc device\n");
goto cleanup_error;
}
- pDrvData->bMwaveDevRegistered = TRUE;
+ pDrvData->bMwaveDevRegistered = true;
pDrvData->sLine = register_serial_portandirq(
pDrvData->rBDData.rDspSettings.usUartBaseIO,
@@ -668,7 +668,7 @@ static int __init mwave_init(void)
if (device_register(&mwave_device))
goto cleanup_error;
- pDrvData->device_registered = TRUE;
+ pDrvData->device_registered = true;
for (i = 0; i < ARRAY_SIZE(mwave_dev_attrs); i++) {
if(device_create_file(&mwave_device, mwave_dev_attrs[i])) {
PRINTK_ERROR(KERN_ERR_MWAVE
diff --git a/drivers/char/mwave/mwavedd.h b/drivers/char/mwave/mwavedd.h
index 7e0d530..37e0a49 100644
--- a/drivers/char/mwave/mwavedd.h
+++ b/drivers/char/mwave/mwavedd.h
@@ -125,8 +125,8 @@ extern int mwave_uart_io;
typedef struct _MWAVE_IPC {
unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */
- BOOLEAN bIsEnabled;
- BOOLEAN bIsHere;
+ bool bIsEnabled;
+ bool bIsHere;
/* entry spin lock */
wait_queue_head_t ipc_wait_queue;
} MWAVE_IPC;
@@ -135,12 +135,12 @@ typedef struct _MWAVE_DEVICE_DATA {
THINKPAD_BD_DATA rBDData; /* board driver's data area */
unsigned long ulIPCSource_ISR; /* IPC source bits for recently processed intr, set during ISR processing */
unsigned long ulIPCSource_DPC; /* IPC source bits for recently processed intr, set during DPC processing */
- BOOLEAN bBDInitialized;
- BOOLEAN bResourcesClaimed;
- BOOLEAN bDSPEnabled;
- BOOLEAN bDSPReset;
+ bool bBDInitialized;
+ bool bResourcesClaimed;
+ bool bDSPEnabled;
+ bool bDSPReset;
MWAVE_IPC IPCs[16];
- BOOLEAN bMwaveDevRegistered;
+ bool bMwaveDevRegistered;
short sLine;
int nr_registered_attrs;
int device_registered;
diff --git a/drivers/char/mwave/smapi.c b/drivers/char/mwave/smapi.c
index 6187fd1..8c5411a 100644
--- a/drivers/char/mwave/smapi.c
+++ b/drivers/char/mwave/smapi.c
@@ -493,7 +493,7 @@ exit_smapi_request_error:
}
-int smapi_set_DSP_power_state(BOOLEAN bOn)
+int smapi_set_DSP_power_state(bool bOn)
{
int bRC = -EIO;
unsigned short usAX, usBX, usCX, usDX, usDI, usSI;
@@ -556,7 +556,7 @@ int smapi_init(void)
PRINTK_ERROR("smapi::smapi_init, ERROR unable to read from SMAPI port\n");
} else {
PRINTK_2(TRACE_SMAPI,
- "smapi::smapi_init, exit TRUE g_usSmapiPort %x\n",
+ "smapi::smapi_init, exit true g_usSmapiPort %x\n",
g_usSmapiPort);
retval = 0;
//SmapiQuerySystemID();
diff --git a/drivers/char/mwave/smapi.h b/drivers/char/mwave/smapi.h
index 64b2ec1..ebc206b 100644
--- a/drivers/char/mwave/smapi.h
+++ b/drivers/char/mwave/smapi.h
@@ -49,10 +49,6 @@
#ifndef _LINUX_SMAPI_H
#define _LINUX_SMAPI_H
-#define TRUE 1
-#define FALSE 0
-#define BOOLEAN int
-
typedef struct {
int bDSPPresent;
int bDSPEnabled;
@@ -74,7 +70,7 @@ typedef struct {
int smapi_init(void);
int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings);
int smapi_set_DSP_cfg(void);
-int smapi_set_DSP_power_state(BOOLEAN bOn);
+int smapi_set_DSP_power_state(bool bOn);
#endif
diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c
index 04e6d6a..5e1618a 100644
--- a/drivers/char/mwave/tp3780i.c
+++ b/drivers/char/mwave/tp3780i.c
@@ -80,13 +80,13 @@ static void EnableSRAM(THINKPAD_BD_DATA * pBDData)
WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode));
MKWORD(rGpioDriverEnable) = 0;
- rGpioDriverEnable.Enable10 = TRUE;
- rGpioDriverEnable.Mask10 = TRUE;
+ rGpioDriverEnable.Enable10 = true;
+ rGpioDriverEnable.Mask10 = true;
WriteMsaCfg(DSP_GpioDriverEnable_15_8, MKWORD(rGpioDriverEnable));
MKWORD(rGpioOutputData) = 0;
rGpioOutputData.Latch10 = 0;
- rGpioOutputData.Mask10 = TRUE;
+ rGpioOutputData.Mask10 = true;
WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData));
PRINTK_1(TRACE_TP3780I, "tp3780i::EnableSRAM exit\n");
@@ -127,7 +127,7 @@ static irqreturn_t DspInterrupt(int irq, void *dev_id)
PRINTK_2(TRACE_TP3780I,
"tp3780i::DspInterrupt usIntCount %x\n",
pDrvData->IPCs[usPCNum - 1].usIntCount);
- if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == TRUE) {
+ if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == true) {
PRINTK_2(TRACE_TP3780I,
"tp3780i::DspInterrupt, waking up usPCNum %x\n",
usPCNum - 1);
@@ -160,8 +160,8 @@ int tp3780I_InitializeBoardData(THINKPAD_BD_DATA * pBDData)
PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_InitializeBoardData entry pBDData %p\n", pBDData);
- pBDData->bDSPEnabled = FALSE;
- pSettings->bInterruptClaimed = FALSE;
+ pBDData->bDSPEnabled = false;
+ pSettings->bInterruptClaimed = false;
retval = smapi_init();
if (retval) {
@@ -269,7 +269,7 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData)
if (pSettings->bInterruptClaimed) {
free_irq(pSettings->usDspIrq, NULL);
- pSettings->bInterruptClaimed = FALSE;
+ pSettings->bInterruptClaimed = false;
}
PRINTK_2(TRACE_TP3780I,
@@ -283,7 +283,7 @@ int tp3780I_ReleaseResources(THINKPAD_BD_DATA * pBDData)
int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
{
DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
- BOOLEAN bDSPPoweredUp = FALSE, bInterruptAllocated = FALSE;
+ bool bDSPPoweredUp = false, bInterruptAllocated = false;
PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP entry pBDData %p\n", pBDData);
@@ -336,14 +336,14 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
}
}
- pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = TRUE;
- pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = TRUE;
+ pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = true;
+ pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = true;
if (pBDData->bShareDspIrq) {
- pSettings->bDspIrqActiveLow = FALSE;
+ pSettings->bDspIrqActiveLow = false;
}
if (pBDData->bShareUartIrq) {
- pSettings->bUartIrqActiveLow = FALSE;
+ pSettings->bUartIrqActiveLow = false;
}
pSettings->usNumTransfers = TP_CFG_NumTransfers;
@@ -373,16 +373,16 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
PRINTK_3(TRACE_TP3780I,
"tp3780i::tp3780I_EnableDSP, got interrupt %x bShareDspIrq %x\n",
pSettings->usDspIrq, pBDData->bShareDspIrq);
- bInterruptAllocated = TRUE;
- pSettings->bInterruptClaimed = TRUE;
+ bInterruptAllocated = true;
+ pSettings->bInterruptClaimed = true;
}
- smapi_set_DSP_power_state(FALSE);
- if (smapi_set_DSP_power_state(TRUE)) {
- PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(TRUE) failed\n");
+ smapi_set_DSP_power_state(false);
+ if (smapi_set_DSP_power_state(true)) {
+ PRINTK_ERROR(KERN_ERR_MWAVE "tp3780i::tp3780I_EnableDSP: Error: smapi_set_DSP_power_state(true) failed\n");
goto exit_cleanup;
} else {
- bDSPPoweredUp = TRUE;
+ bDSPPoweredUp = true;
}
if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) {
@@ -392,7 +392,7 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
EnableSRAM(pBDData);
- pBDData->bDSPEnabled = TRUE;
+ pBDData->bDSPEnabled = true;
PRINTK_1(TRACE_TP3780I, "tp3780i::tp3780I_EnableDSP exit\n");
@@ -401,10 +401,10 @@ int tp3780I_EnableDSP(THINKPAD_BD_DATA * pBDData)
exit_cleanup:
PRINTK_ERROR("tp3780i::tp3780I_EnableDSP: Cleaning up\n");
if (bDSPPoweredUp)
- smapi_set_DSP_power_state(FALSE);
+ smapi_set_DSP_power_state(false);
if (bInterruptAllocated) {
free_irq(pSettings->usDspIrq, NULL);
- pSettings->bInterruptClaimed = FALSE;
+ pSettings->bInterruptClaimed = false;
}
return -EIO;
}
@@ -421,10 +421,10 @@ int tp3780I_DisableDSP(THINKPAD_BD_DATA * pBDData)
dsp3780I_DisableDSP(&pBDData->rDspSettings);
if (pSettings->bInterruptClaimed) {
free_irq(pSettings->usDspIrq, NULL);
- pSettings->bInterruptClaimed = FALSE;
+ pSettings->bInterruptClaimed = false;
}
- smapi_set_DSP_power_state(FALSE);
- pBDData->bDSPEnabled = FALSE;
+ smapi_set_DSP_power_state(false);
+ pBDData->bDSPEnabled = false;
}
PRINTK_2(TRACE_TP3780I, "tp3780i::tp3780I_DisableDSP exit retval %x\n", retval);
@@ -516,7 +516,7 @@ int tp3780I_ReadWriteDspDStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
int retval = 0;
DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- BOOLEAN bRC = 0;
+ bool bRC = 0;
PRINTK_6(TRACE_TP3780I,
"tp3780i::tp3780I_ReadWriteDspDStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n",
@@ -552,7 +552,7 @@ int tp3780I_ReadWriteDspIStore(THINKPAD_BD_DATA * pBDData, unsigned int uOpcode,
int retval = 0;
DSP_3780I_CONFIG_SETTINGS *pSettings = &pBDData->rDspSettings;
unsigned short usDspBaseIO = pSettings->usDspBaseIO;
- BOOLEAN bRC = 0;
+ bool bRC = 0;
PRINTK_6(TRACE_TP3780I,
"tp3780i::tp3780I_ReadWriteDspIStore entry pBDData %p, uOpcode %x, pvBuffer %p, uCount %x, ulDSPAddr %lx\n",
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index f8a483c..d233688 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -286,7 +286,7 @@ static int register_device(int minor, struct pp_struct *pp)
struct parport *port;
struct pardevice *pdev = NULL;
char *name;
- int fl;
+ struct pardev_cb ppdev_cb;
name = kasprintf(GFP_KERNEL, CHRDEV "%x", minor);
if (name == NULL)
@@ -299,9 +299,11 @@ static int register_device(int minor, struct pp_struct *pp)
return -ENXIO;
}
- fl = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
- pdev = parport_register_device(port, name, NULL,
- NULL, pp_irq, fl, pp);
+ memset(&ppdev_cb, 0, sizeof(ppdev_cb));
+ ppdev_cb.irq_func = pp_irq;
+ ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
+ ppdev_cb.private = pp;
+ pdev = parport_register_dev_model(port, name, &ppdev_cb, minor);
parport_put_port(port);
if (!pdev) {
@@ -799,10 +801,23 @@ static void pp_detach(struct parport *port)
device_destroy(ppdev_class, MKDEV(PP_MAJOR, port->number));
}
+static int pp_probe(struct pardevice *par_dev)
+{
+ struct device_driver *drv = par_dev->dev.driver;
+ int len = strlen(drv->name);
+
+ if (strncmp(par_dev->name, drv->name, len))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver pp_driver = {
.name = CHRDEV,
- .attach = pp_attach,
+ .probe = pp_probe,
+ .match_port = pp_attach,
.detach = pp_detach,
+ .devmodel = true,
};
static int __init ppdev_init(void)
diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c
index 94006f9..10e5632 100644
--- a/drivers/char/snsc.c
+++ b/drivers/char/snsc.c
@@ -385,13 +385,18 @@ scdrv_init(void)
event_nasid = ia64_sn_get_console_nasid();
+ snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME);
+ if (IS_ERR(snsc_class)) {
+ printk("%s: failed to allocate class\n", __func__);
+ return PTR_ERR(snsc_class);
+ }
+
if (alloc_chrdev_region(&first_dev, 0, num_cnodes,
SYSCTL_BASENAME) < 0) {
printk("%s: failed to register SN system controller device\n",
__func__);
return -ENODEV;
}
- snsc_class = class_create(THIS_MODULE, SYSCTL_BASENAME);
for (cnode = 0; cnode < num_cnodes; cnode++) {
geoid = cnodeid_get_geoid(cnode);
diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c
index 69f6b4a..398800e 100644
--- a/drivers/char/tile-srom.c
+++ b/drivers/char/tile-srom.c
@@ -331,13 +331,11 @@ static const struct file_operations srom_fops = {
/**
* srom_setup_minor() - Initialize per-minor information.
* @srom: Per-device SROM state.
- * @index: Device to set up.
+ * @devhdl: Partition device handle.
*/
-static int srom_setup_minor(struct srom_dev *srom, int index)
+static int srom_setup_minor(struct srom_dev *srom, int devhdl)
{
- struct device *dev;
- int devhdl = srom->hv_devhdl;
-
+ srom->hv_devhdl = devhdl;
mutex_init(&srom->lock);
if (_srom_read(devhdl, &srom->total_size,
@@ -350,9 +348,7 @@ static int srom_setup_minor(struct srom_dev *srom, int index)
SROM_PAGE_SIZE_OFF, sizeof(srom->page_size)) < 0)
return -EIO;
- dev = device_create(srom_class, &srom_parent->dev,
- MKDEV(srom_major, index), srom, "%d", index);
- return PTR_ERR_OR_ZERO(dev);
+ return 0;
}
/** srom_init() - Initialize the driver's module. */
@@ -365,7 +361,7 @@ static int srom_init(void)
* Start with a plausible number of partitions; the krealloc() call
* below will yield about log(srom_devs) additional allocations.
*/
- srom_devices = kzalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
+ srom_devices = kmalloc(4 * sizeof(struct srom_dev), GFP_KERNEL);
/* Discover the number of srom partitions. */
for (i = 0; ; i++) {
@@ -373,7 +369,7 @@ static int srom_init(void)
char buf[20];
struct srom_dev *new_srom_devices =
krealloc(srom_devices, (i+1) * sizeof(struct srom_dev),
- GFP_KERNEL | __GFP_ZERO);
+ GFP_KERNEL);
if (!new_srom_devices) {
result = -ENOMEM;
goto fail_mem;
@@ -387,7 +383,9 @@ static int srom_init(void)
i, devhdl);
break;
}
- srom_devices[i].hv_devhdl = devhdl;
+ result = srom_setup_minor(&srom_devices[i], devhdl);
+ if (result != 0)
+ goto fail_mem;
}
srom_devs = i;
@@ -431,9 +429,13 @@ static int srom_init(void)
srom_class->dev_groups = srom_dev_groups;
srom_class->devnode = srom_devnode;
- /* Do per-partition initialization */
+ /* Create per-partition devices */
for (i = 0; i < srom_devs; i++) {
- result = srom_setup_minor(srom_devices + i, i);
+ struct device *dev =
+ device_create(srom_class, &srom_parent->dev,
+ MKDEV(srom_major, i), srom_devices + i,
+ "%d", i);
+ result = PTR_ERR_OR_ZERO(dev);
if (result < 0)
goto fail_class;
}
diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c
index b098d2d..67549ce 100644
--- a/drivers/char/ttyprintk.c
+++ b/drivers/char/ttyprintk.c
@@ -31,60 +31,53 @@ static struct ttyprintk_port tpk_port;
* printk messages (also suitable for logging service):
* - any cr is replaced by nl
* - adds a ttyprintk source tag in front of each line
- * - too long message is fragmeted, with '\'nl between fragments
- * - TPK_STR_SIZE isn't really the write_room limiting factor, bcause
+ * - too long message is fragmented, with '\'nl between fragments
+ * - TPK_STR_SIZE isn't really the write_room limiting factor, because
* it is emptied on the fly during preformatting.
*/
#define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
#define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */
-static const char *tpk_tag = "[U] "; /* U for User */
static int tpk_curr;
+static char tpk_buffer[TPK_STR_SIZE + 4];
+
+static void tpk_flush(void)
+{
+ if (tpk_curr > 0) {
+ tpk_buffer[tpk_curr] = '\0';
+ pr_info("[U] %s\n", tpk_buffer);
+ tpk_curr = 0;
+ }
+}
+
static int tpk_printk(const unsigned char *buf, int count)
{
- static char tmp[TPK_STR_SIZE + 4];
int i = tpk_curr;
if (buf == NULL) {
- /* flush tmp[] */
- if (tpk_curr > 0) {
- /* non nl or cr terminated message - add nl */
- tmp[tpk_curr + 0] = '\n';
- tmp[tpk_curr + 1] = '\0';
- printk(KERN_INFO "%s%s", tpk_tag, tmp);
- tpk_curr = 0;
- }
+ tpk_flush();
return i;
}
for (i = 0; i < count; i++) {
- tmp[tpk_curr] = buf[i];
- if (tpk_curr < TPK_STR_SIZE) {
- switch (buf[i]) {
- case '\r':
- /* replace cr with nl */
- tmp[tpk_curr + 0] = '\n';
- tmp[tpk_curr + 1] = '\0';
- printk(KERN_INFO "%s%s", tpk_tag, tmp);
- tpk_curr = 0;
- if ((i + 1) < count && buf[i + 1] == '\n')
- i++;
- break;
- case '\n':
- tmp[tpk_curr + 1] = '\0';
- printk(KERN_INFO "%s%s", tpk_tag, tmp);
- tpk_curr = 0;
- break;
- default:
- tpk_curr++;
- }
- } else {
+ if (tpk_curr >= TPK_STR_SIZE) {
/* end of tmp buffer reached: cut the message in two */
- tmp[tpk_curr + 1] = '\\';
- tmp[tpk_curr + 2] = '\n';
- tmp[tpk_curr + 3] = '\0';
- printk(KERN_INFO "%s%s", tpk_tag, tmp);
- tpk_curr = 0;
+ tpk_buffer[tpk_curr++] = '\\';
+ tpk_flush();
+ }
+
+ switch (buf[i]) {
+ case '\r':
+ tpk_flush();
+ if ((i + 1) < count && buf[i + 1] == '\n')
+ i++;
+ break;
+ case '\n':
+ tpk_flush();
+ break;
+ default:
+ tpk_buffer[tpk_curr++] = buf[i];
+ break;
}
}
diff --git a/drivers/char/xillybus/xillybus_core.c b/drivers/char/xillybus/xillybus_core.c
index dcd19f3..b6c9cde 100644
--- a/drivers/char/xillybus/xillybus_core.c
+++ b/drivers/char/xillybus/xillybus_core.c
@@ -655,10 +655,10 @@ static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
version = channel->wr_buffers[0]->addr;
- /* Check version number. Accept anything below 0x82 for now. */
+ /* Check version number. Reject anything above 0x82. */
if (*version > 0x82) {
dev_err(endpoint->dev,
- "No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgarde. Aborting.\n",
+ "No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgrade. Aborting.\n",
*version);
return -ENODEV;
}
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index e2d9bd7..6a8ac04 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -31,22 +31,12 @@ config COMMON_CLK_WM831X
source "drivers/clk/versatile/Kconfig"
-config COMMON_CLK_MAX_GEN
- bool
-
config COMMON_CLK_MAX77686
- tristate "Clock driver for Maxim 77686 MFD"
- depends on MFD_MAX77686
- select COMMON_CLK_MAX_GEN
- ---help---
- This driver supports Maxim 77686 crystal oscillator clock.
-
-config COMMON_CLK_MAX77802
- tristate "Clock driver for Maxim 77802 PMIC"
- depends on MFD_MAX77686
- select COMMON_CLK_MAX_GEN
+ tristate "Clock driver for Maxim 77620/77686/77802 MFD"
+ depends on MFD_MAX77686 || MFD_MAX77620
---help---
- This driver supports Maxim 77802 crystal oscillator clock.
+ This driver supports Maxim 77620/77686/77802 crystal oscillator
+ clock.
config COMMON_CLK_RK808
tristate "Clock driver for RK808/RK818"
@@ -210,6 +200,7 @@ config COMMON_CLK_OXNAS
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/hisilicon/Kconfig"
+source "drivers/clk/mediatek/Kconfig"
source "drivers/clk/meson/Kconfig"
source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/qcom/Kconfig"
@@ -218,5 +209,6 @@ source "drivers/clk/samsung/Kconfig"
source "drivers/clk/sunxi-ng/Kconfig"
source "drivers/clk/tegra/Kconfig"
source "drivers/clk/ti/Kconfig"
+source "drivers/clk/uniphier/Kconfig"
endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 3b6f9cf..925081e 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -26,10 +26,7 @@ obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
-obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o
-obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
-obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o
obj-$(CONFIG_ARCH_MB86S7X) += clk-mb86s7x.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
@@ -63,6 +60,7 @@ obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_MACH_INGENIC) += ingenic/
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
+obj-$(CONFIG_MACH_LOONGSON32) += loongson1/
obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += meson/
obj-$(CONFIG_MACH_PIC32) += microchip/
@@ -86,6 +84,7 @@ obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_SUNXI) += sunxi-ng/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-y += ti/
+obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/
obj-$(CONFIG_X86) += x86/
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 7f6bec8..4e1cd5a 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -233,14 +233,16 @@ static void clk_generated_startup(struct clk_generated *gck)
>> AT91_PMC_PCR_GCKDIV_OFFSET;
}
-static struct clk * __init
-at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, const char
- *name, const char **parent_names, u8 num_parents,
- u8 id, const struct clk_range *range)
+static struct clk_hw * __init
+at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char **parent_names,
+ u8 num_parents, u8 id,
+ const struct clk_range *range)
{
struct clk_generated *gck;
- struct clk *clk = NULL;
struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
gck = kzalloc(sizeof(*gck), GFP_KERNEL);
if (!gck)
@@ -258,13 +260,15 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, const char
gck->lock = lock;
gck->range = *range;
- clk = clk_register(NULL, &gck->hw);
- if (IS_ERR(clk))
+ hw = &gck->hw;
+ ret = clk_hw_register(NULL, &gck->hw);
+ if (ret) {
kfree(gck);
- else
+ hw = ERR_PTR(ret);
+ } else
clk_generated_startup(gck);
- return clk;
+ return hw;
}
static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
@@ -272,7 +276,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
int num;
u32 id;
const char *name;
- struct clk *clk;
+ struct clk_hw *hw;
unsigned int num_parents;
const char *parent_names[GENERATED_SOURCE_MAX];
struct device_node *gcknp;
@@ -306,13 +310,13 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
of_at91_get_clk_range(gcknp, "atmel,clk-output-range",
&range);
- clk = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
parent_names, num_parents,
id, &range);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
continue;
- of_clk_add_provider(gcknp, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(gcknp, of_clk_hw_simple_get, hw);
}
}
CLK_OF_DECLARE(of_sama5d2_clk_generated_setup, "atmel,sama5d2-clk-generated",
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
index 8e20c8a..e0daa4a 100644
--- a/drivers/clk/at91/clk-h32mx.c
+++ b/drivers/clk/at91/clk-h32mx.c
@@ -92,7 +92,7 @@ static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np)
struct clk_init_data init;
const char *parent_name;
struct regmap *regmap;
- struct clk *clk;
+ int ret;
regmap = syscon_node_to_regmap(of_get_parent(np));
if (IS_ERR(regmap))
@@ -113,13 +113,13 @@ static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np)
h32mxclk->hw.init = &init;
h32mxclk->regmap = regmap;
- clk = clk_register(NULL, &h32mxclk->hw);
- if (IS_ERR(clk)) {
+ ret = clk_hw_register(NULL, &h32mxclk->hw);
+ if (ret) {
kfree(h32mxclk);
return;
}
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, &h32mxclk->hw);
}
CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx",
of_sama5d4_clk_h32mx_setup);
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 58b5bac..c813c27 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -128,15 +128,16 @@ static const struct clk_ops main_osc_ops = {
.is_prepared = clk_main_osc_is_prepared,
};
-static struct clk * __init
+static 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 *clk = NULL;
struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !parent_name)
return ERR_PTR(-EINVAL);
@@ -160,16 +161,19 @@ at91_clk_register_main_osc(struct regmap *regmap,
AT91_PMC_MOSCEN,
AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
- clk = clk_register(NULL, &osc->hw);
- if (IS_ERR(clk))
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
+ if (ret) {
kfree(osc);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
const char *name = np->name;
const char *parent_name;
struct regmap *regmap;
@@ -183,11 +187,11 @@ static void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
- if (IS_ERR(clk))
+ hw = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc",
of_at91rm9200_clk_main_osc_setup);
@@ -271,14 +275,15 @@ static const struct clk_ops main_rc_osc_ops = {
.recalc_accuracy = clk_main_rc_osc_recalc_accuracy,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_main_rc_osc(struct regmap *regmap,
const char *name,
u32 frequency, u32 accuracy)
{
struct clk_main_rc_osc *osc;
- struct clk *clk = NULL;
struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !frequency)
return ERR_PTR(-EINVAL);
@@ -298,16 +303,19 @@ at91_clk_register_main_rc_osc(struct regmap *regmap,
osc->frequency = frequency;
osc->accuracy = accuracy;
- clk = clk_register(NULL, &osc->hw);
- if (IS_ERR(clk))
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, hw);
+ if (ret) {
kfree(osc);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
u32 frequency = 0;
u32 accuracy = 0;
const char *name = np->name;
@@ -321,11 +329,11 @@ static void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- clk = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy);
- if (IS_ERR(clk))
+ hw = at91_clk_register_main_rc_osc(regmap, name, frequency, accuracy);
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc",
of_at91sam9x5_clk_main_rc_osc_setup);
@@ -395,14 +403,15 @@ static const struct clk_ops rm9200_main_ops = {
.recalc_rate = clk_rm9200_main_recalc_rate,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
const char *parent_name)
{
struct clk_rm9200_main *clkmain;
- struct clk *clk = NULL;
struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name)
return ERR_PTR(-EINVAL);
@@ -423,16 +432,19 @@ at91_clk_register_rm9200_main(struct regmap *regmap,
clkmain->hw.init = &init;
clkmain->regmap = regmap;
- clk = clk_register(NULL, &clkmain->hw);
- if (IS_ERR(clk))
+ hw = &clkmain->hw;
+ ret = clk_hw_register(NULL, &clkmain->hw);
+ if (ret) {
kfree(clkmain);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91rm9200_clk_main_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
const char *parent_name;
const char *name = np->name;
struct regmap *regmap;
@@ -444,11 +456,11 @@ static void __init of_at91rm9200_clk_main_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- clk = at91_clk_register_rm9200_main(regmap, name, parent_name);
- if (IS_ERR(clk))
+ hw = at91_clk_register_rm9200_main(regmap, name, parent_name);
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main",
of_at91rm9200_clk_main_setup);
@@ -529,16 +541,17 @@ static const struct clk_ops sam9x5_main_ops = {
.get_parent = clk_sam9x5_main_get_parent,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_sam9x5_main(struct regmap *regmap,
const char *name,
const char **parent_names,
int num_parents)
{
struct clk_sam9x5_main *clkmain;
- struct clk *clk = NULL;
struct clk_init_data init;
unsigned int status;
+ struct clk_hw *hw;
+ int ret;
if (!name)
return ERR_PTR(-EINVAL);
@@ -561,16 +574,19 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0;
- clk = clk_register(NULL, &clkmain->hw);
- if (IS_ERR(clk))
+ hw = &clkmain->hw;
+ ret = clk_hw_register(NULL, &clkmain->hw);
+ if (ret) {
kfree(clkmain);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
const char *parent_names[2];
unsigned int num_parents;
const char *name = np->name;
@@ -587,12 +603,12 @@ static void __init of_at91sam9x5_clk_main_setup(struct device_node *np)
of_property_read_string(np, "clock-output-names", &name);
- clk = at91_clk_register_sam9x5_main(regmap, name, parent_names,
+ hw = at91_clk_register_sam9x5_main(regmap, name, parent_names,
num_parents);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main",
of_at91sam9x5_clk_main_setup);
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index d1021e1..e9cba9f 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -120,7 +120,7 @@ static const struct clk_ops master_ops = {
.get_parent = clk_master_get_parent,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_master(struct regmap *regmap,
const char *name, int num_parents,
const char **parent_names,
@@ -128,8 +128,9 @@ at91_clk_register_master(struct regmap *regmap,
const struct clk_master_characteristics *characteristics)
{
struct clk_master *master;
- struct clk *clk = NULL;
struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !num_parents || !parent_names)
return ERR_PTR(-EINVAL);
@@ -149,12 +150,14 @@ at91_clk_register_master(struct regmap *regmap,
master->characteristics = characteristics;
master->regmap = regmap;
- clk = clk_register(NULL, &master->hw);
- if (IS_ERR(clk)) {
+ hw = &master->hw;
+ ret = clk_hw_register(NULL, &master->hw);
+ if (ret) {
kfree(master);
+ hw = ERR_PTR(ret);
}
- return clk;
+ return hw;
}
@@ -198,7 +201,7 @@ static void __init
of_at91_clk_master_setup(struct device_node *np,
const struct clk_master_layout *layout)
{
- struct clk *clk;
+ struct clk_hw *hw;
unsigned int num_parents;
const char *parent_names[MASTER_SOURCE_MAX];
const char *name = np->name;
@@ -221,13 +224,13 @@ of_at91_clk_master_setup(struct device_node *np,
if (IS_ERR(regmap))
return;
- clk = at91_clk_register_master(regmap, name, num_parents,
+ hw = at91_clk_register_master(regmap, name, num_parents,
parent_names, layout,
characteristics);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto out_free_characteristics;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
return;
out_free_characteristics:
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
index fd16072..dc29fd9 100644
--- a/drivers/clk/at91/clk-peripheral.c
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -104,13 +104,14 @@ static const struct clk_ops peripheral_ops = {
.is_enabled = clk_peripheral_is_enabled,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_peripheral(struct regmap *regmap, const char *name,
const char *parent_name, u32 id)
{
struct clk_peripheral *periph;
- struct clk *clk = NULL;
struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
return ERR_PTR(-EINVAL);
@@ -129,11 +130,14 @@ at91_clk_register_peripheral(struct regmap *regmap, const char *name,
periph->hw.init = &init;
periph->regmap = regmap;
- clk = clk_register(NULL, &periph->hw);
- if (IS_ERR(clk))
+ hw = &periph->hw;
+ ret = clk_hw_register(NULL, &periph->hw);
+ if (ret) {
kfree(periph);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
@@ -327,14 +331,15 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.set_rate = clk_sam9x5_peripheral_set_rate,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name,
u32 id, const struct clk_range *range)
{
struct clk_sam9x5_peripheral *periph;
- struct clk *clk = NULL;
struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
if (!name || !parent_name)
return ERR_PTR(-EINVAL);
@@ -357,13 +362,15 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
periph->auto_div = true;
periph->range = *range;
- clk = clk_register(NULL, &periph->hw);
- if (IS_ERR(clk))
+ hw = &periph->hw;
+ ret = clk_hw_register(NULL, &periph->hw);
+ if (ret) {
kfree(periph);
- else
+ hw = ERR_PTR(ret);
+ } else
clk_sam9x5_peripheral_autodiv(periph);
- return clk;
+ return hw;
}
static void __init
@@ -371,7 +378,7 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
{
int num;
u32 id;
- struct clk *clk;
+ struct clk_hw *hw;
const char *parent_name;
const char *name;
struct device_node *periphclknp;
@@ -400,7 +407,7 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
name = periphclknp->name;
if (type == PERIPHERAL_AT91RM9200) {
- clk = at91_clk_register_peripheral(regmap, name,
+ hw = at91_clk_register_peripheral(regmap, name,
parent_name, id);
} else {
struct clk_range range = CLK_RANGE(0, 0);
@@ -409,17 +416,17 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
"atmel,clk-output-range",
&range);
- clk = at91_clk_register_sam9x5_peripheral(regmap,
+ hw = at91_clk_register_sam9x5_peripheral(regmap,
&pmc_pcr_lock,
name,
parent_name,
id, &range);
}
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
continue;
- of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(periphclknp, of_clk_hw_simple_get, hw);
}
}
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index fb2e0b5..45ad168 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -296,17 +296,18 @@ static const struct clk_ops pll_ops = {
.set_rate = clk_pll_set_rate,
};
-static struct clk * __init
+static 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 *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
int offset = PLL_REG(id);
unsigned int pllr;
+ int ret;
if (id > PLL_MAX_ID)
return ERR_PTR(-EINVAL);
@@ -330,12 +331,14 @@ at91_clk_register_pll(struct regmap *regmap, const char *name,
pll->div = PLL_DIV(pllr);
pll->mul = PLL_MUL(pllr, layout);
- clk = clk_register(NULL, &pll->hw);
- if (IS_ERR(clk)) {
+ hw = &pll->hw;
+ ret = clk_hw_register(NULL, &pll->hw);
+ if (ret) {
kfree(pll);
+ hw = ERR_PTR(ret);
}
- return clk;
+ return hw;
}
@@ -465,7 +468,7 @@ of_at91_clk_pll_setup(struct device_node *np,
const struct clk_pll_layout *layout)
{
u32 id;
- struct clk *clk;
+ struct clk_hw *hw;
struct regmap *regmap;
const char *parent_name;
const char *name = np->name;
@@ -486,12 +489,12 @@ of_at91_clk_pll_setup(struct device_node *np,
if (!characteristics)
return;
- clk = at91_clk_register_pll(regmap, name, parent_name, id, layout,
+ hw = at91_clk_register_pll(regmap, name, parent_name, id, layout,
characteristics);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto out_free_characteristics;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
return;
out_free_characteristics:
diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c
index 2bed264..b4afaf2 100644
--- a/drivers/clk/at91/clk-plldiv.c
+++ b/drivers/clk/at91/clk-plldiv.c
@@ -75,13 +75,14 @@ static const struct clk_ops plldiv_ops = {
.set_rate = clk_plldiv_set_rate,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_plldiv(struct regmap *regmap, const char *name,
const char *parent_name)
{
struct clk_plldiv *plldiv;
- struct clk *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL);
if (!plldiv)
@@ -96,18 +97,20 @@ at91_clk_register_plldiv(struct regmap *regmap, const char *name,
plldiv->hw.init = &init;
plldiv->regmap = regmap;
- clk = clk_register(NULL, &plldiv->hw);
-
- if (IS_ERR(clk))
+ hw = &plldiv->hw;
+ ret = clk_hw_register(NULL, &plldiv->hw);
+ if (ret) {
kfree(plldiv);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init
of_at91sam9x5_clk_plldiv_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
const char *parent_name;
const char *name = np->name;
struct regmap *regmap;
@@ -120,12 +123,11 @@ of_at91sam9x5_clk_plldiv_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- clk = at91_clk_register_plldiv(regmap, name, parent_name);
- if (IS_ERR(clk))
+ hw = at91_clk_register_plldiv(regmap, name, parent_name);
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
- return;
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",
of_at91sam9x5_clk_plldiv_setup);
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
index 25d5906..190122e64 100644
--- a/drivers/clk/at91/clk-programmable.c
+++ b/drivers/clk/at91/clk-programmable.c
@@ -170,15 +170,16 @@ static const struct clk_ops programmable_ops = {
.set_rate = clk_programmable_set_rate,
};
-static struct clk * __init
+static 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)
{
struct clk_programmable *prog;
- struct clk *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
if (id > PROG_ID_MAX)
return ERR_PTR(-EINVAL);
@@ -198,11 +199,14 @@ at91_clk_register_programmable(struct regmap *regmap,
prog->hw.init = &init;
prog->regmap = regmap;
- clk = clk_register(NULL, &prog->hw);
- if (IS_ERR(clk))
+ hw = &prog->hw;
+ ret = clk_hw_register(NULL, &prog->hw);
+ if (ret) {
kfree(prog);
+ hw = &prog->hw;
+ }
- return clk;
+ return hw;
}
static const struct clk_programmable_layout at91rm9200_programmable_layout = {
@@ -229,7 +233,7 @@ of_at91_clk_prog_setup(struct device_node *np,
{
int num;
u32 id;
- struct clk *clk;
+ struct clk_hw *hw;
unsigned int num_parents;
const char *parent_names[PROG_SOURCE_MAX];
const char *name;
@@ -257,13 +261,13 @@ of_at91_clk_prog_setup(struct device_node *np,
if (of_property_read_string(np, "clock-output-names", &name))
name = progclknp->name;
- clk = at91_clk_register_programmable(regmap, name,
+ hw = at91_clk_register_programmable(regmap, name,
parent_names, num_parents,
id, layout);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
continue;
- of_clk_add_provider(progclknp, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(progclknp, of_clk_hw_simple_get, hw);
}
}
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c
index 61090b1..560a8b9 100644
--- a/drivers/clk/at91/clk-slow.c
+++ b/drivers/clk/at91/clk-slow.c
@@ -13,42 +13,11 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
-#include <linux/delay.h>
#include <linux/of.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include "pmc.h"
-#include "sckc.h"
-
-#define SLOW_CLOCK_FREQ 32768
-#define SLOWCK_SW_CYCLES 5
-#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
- SLOW_CLOCK_FREQ)
-
-#define AT91_SCKC_CR 0x00
-#define AT91_SCKC_RCEN (1 << 0)
-#define AT91_SCKC_OSC32EN (1 << 1)
-#define AT91_SCKC_OSC32BYP (1 << 2)
-#define AT91_SCKC_OSCSEL (1 << 3)
-
-struct clk_slow_osc {
- struct clk_hw hw;
- void __iomem *sckcr;
- unsigned long startup_usec;
-};
-
-#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
-
-struct clk_slow_rc_osc {
- struct clk_hw hw;
- void __iomem *sckcr;
- unsigned long frequency;
- unsigned long accuracy;
- unsigned long startup_usec;
-};
-
-#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
struct clk_sam9260_slow {
struct clk_hw hw;
@@ -57,328 +26,6 @@ struct clk_sam9260_slow {
#define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw)
-struct clk_sam9x5_slow {
- struct clk_hw hw;
- void __iomem *sckcr;
- u8 parent;
-};
-
-#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, 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;
- u32 tmp = readl(sckcr);
-
- if (tmp & AT91_SCKC_OSC32BYP)
- return 0;
-
- writel(tmp | AT91_SCKC_OSC32EN, sckcr);
-
- usleep_range(osc->startup_usec, osc->startup_usec + 1);
-
- return 0;
-}
-
-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;
- u32 tmp = readl(sckcr);
-
- if (tmp & AT91_SCKC_OSC32BYP)
- return;
-
- writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
-}
-
-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;
- u32 tmp = readl(sckcr);
-
- if (tmp & AT91_SCKC_OSC32BYP)
- return 1;
-
- return !!(tmp & AT91_SCKC_OSC32EN);
-}
-
-static const struct clk_ops slow_osc_ops = {
- .prepare = clk_slow_osc_prepare,
- .unprepare = clk_slow_osc_unprepare,
- .is_prepared = clk_slow_osc_is_prepared,
-};
-
-static struct clk * __init
-at91_clk_register_slow_osc(void __iomem *sckcr,
- const char *name,
- const char *parent_name,
- unsigned long startup,
- bool bypass)
-{
- struct clk_slow_osc *osc;
- struct clk *clk = NULL;
- struct clk_init_data init;
-
- if (!sckcr || !name || !parent_name)
- return ERR_PTR(-EINVAL);
-
- osc = kzalloc(sizeof(*osc), GFP_KERNEL);
- if (!osc)
- return ERR_PTR(-ENOMEM);
-
- 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;
-
- if (bypass)
- writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
- sckcr);
-
- clk = clk_register(NULL, &osc->hw);
- if (IS_ERR(clk))
- kfree(osc);
-
- return clk;
-}
-
-void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np,
- void __iomem *sckcr)
-{
- struct clk *clk;
- const char *parent_name;
- const char *name = np->name;
- u32 startup;
- bool bypass;
-
- parent_name = of_clk_get_parent_name(np, 0);
- of_property_read_string(np, "clock-output-names", &name);
- of_property_read_u32(np, "atmel,startup-time-usec", &startup);
- bypass = of_property_read_bool(np, "atmel,osc-bypass");
-
- clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
- bypass);
- if (IS_ERR(clk))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-
-static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
-
- return osc->frequency;
-}
-
-static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
- unsigned long parent_acc)
-{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
-
- return osc->accuracy;
-}
-
-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;
-
- writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
-
- usleep_range(osc->startup_usec, osc->startup_usec + 1);
-
- return 0;
-}
-
-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;
-
- writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
-}
-
-static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
-{
- struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
-
- return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
-}
-
-static const struct clk_ops slow_rc_osc_ops = {
- .prepare = clk_slow_rc_osc_prepare,
- .unprepare = clk_slow_rc_osc_unprepare,
- .is_prepared = clk_slow_rc_osc_is_prepared,
- .recalc_rate = clk_slow_rc_osc_recalc_rate,
- .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
-};
-
-static struct clk * __init
-at91_clk_register_slow_rc_osc(void __iomem *sckcr,
- const char *name,
- unsigned long frequency,
- unsigned long accuracy,
- unsigned long startup)
-{
- struct clk_slow_rc_osc *osc;
- struct clk *clk = NULL;
- struct clk_init_data init;
-
- if (!sckcr || !name)
- return ERR_PTR(-EINVAL);
-
- 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->frequency = frequency;
- osc->accuracy = accuracy;
- osc->startup_usec = startup;
-
- clk = clk_register(NULL, &osc->hw);
- if (IS_ERR(clk))
- kfree(osc);
-
- return clk;
-}
-
-void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np,
- void __iomem *sckcr)
-{
- struct clk *clk;
- u32 frequency = 0;
- u32 accuracy = 0;
- u32 startup = 0;
- const char *name = np->name;
-
- of_property_read_string(np, "clock-output-names", &name);
- of_property_read_u32(np, "clock-frequency", &frequency);
- of_property_read_u32(np, "clock-accuracy", &accuracy);
- of_property_read_u32(np, "atmel,startup-time-usec", &startup);
-
- clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy,
- startup);
- if (IS_ERR(clk))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-
-static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
-{
- struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
- void __iomem *sckcr = slowck->sckcr;
- u32 tmp;
-
- if (index > 1)
- return -EINVAL;
-
- tmp = readl(sckcr);
-
- if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
- (index && (tmp & AT91_SCKC_OSCSEL)))
- return 0;
-
- if (index)
- tmp |= AT91_SCKC_OSCSEL;
- else
- tmp &= ~AT91_SCKC_OSCSEL;
-
- writel(tmp, sckcr);
-
- usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
-
- return 0;
-}
-
-static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
-{
- struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
-
- return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
-}
-
-static const struct clk_ops sam9x5_slow_ops = {
- .set_parent = clk_sam9x5_slow_set_parent,
- .get_parent = clk_sam9x5_slow_get_parent,
-};
-
-static struct clk * __init
-at91_clk_register_sam9x5_slow(void __iomem *sckcr,
- const char *name,
- const char **parent_names,
- int num_parents)
-{
- struct clk_sam9x5_slow *slowck;
- struct clk *clk = NULL;
- struct clk_init_data init;
-
- if (!sckcr || !name || !parent_names || !num_parents)
- return ERR_PTR(-EINVAL);
-
- 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;
-
- slowck->hw.init = &init;
- slowck->sckcr = sckcr;
- slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
-
- clk = clk_register(NULL, &slowck->hw);
- if (IS_ERR(clk))
- kfree(slowck);
-
- return clk;
-}
-
-void __init of_at91sam9x5_clk_slow_setup(struct device_node *np,
- void __iomem *sckcr)
-{
- struct clk *clk;
- const char *parent_names[2];
- unsigned int num_parents;
- const char *name = np->name;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > 2)
- return;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- of_property_read_string(np, "clock-output-names", &name);
-
- clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
- num_parents);
- if (IS_ERR(clk))
- return;
-
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
-}
-
static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw)
{
struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw);
@@ -393,15 +40,16 @@ static const struct clk_ops sam9260_slow_ops = {
.get_parent = clk_sam9260_slow_get_parent,
};
-static struct clk * __init
+static 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 *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
if (!name)
return ERR_PTR(-EINVAL);
@@ -422,16 +70,19 @@ at91_clk_register_sam9260_slow(struct regmap *regmap,
slowck->hw.init = &init;
slowck->regmap = regmap;
- clk = clk_register(NULL, &slowck->hw);
- if (IS_ERR(clk))
+ hw = &slowck->hw;
+ ret = clk_hw_register(NULL, &slowck->hw);
+ if (ret) {
kfree(slowck);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91sam9260_clk_slow_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
const char *parent_names[2];
unsigned int num_parents;
const char *name = np->name;
@@ -448,12 +99,12 @@ static void __init of_at91sam9260_clk_slow_setup(struct device_node *np)
of_property_read_string(np, "clock-output-names", &name);
- clk = at91_clk_register_sam9260_slow(regmap, name, parent_names,
+ hw = at91_clk_register_sam9260_slow(regmap, name, parent_names,
num_parents);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow",
diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c
index 3c04b06..965c662 100644
--- a/drivers/clk/at91/clk-smd.c
+++ b/drivers/clk/at91/clk-smd.c
@@ -111,13 +111,14 @@ static const struct clk_ops at91sam9x5_smd_ops = {
.set_rate = at91sam9x5_clk_smd_set_rate,
};
-static struct clk * __init
+static 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 *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
smd = kzalloc(sizeof(*smd), GFP_KERNEL);
if (!smd)
@@ -132,16 +133,19 @@ at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
smd->hw.init = &init;
smd->regmap = regmap;
- clk = clk_register(NULL, &smd->hw);
- if (IS_ERR(clk))
+ hw = &smd->hw;
+ ret = clk_hw_register(NULL, &smd->hw);
+ if (ret) {
kfree(smd);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
unsigned int num_parents;
const char *parent_names[SMD_SOURCE_MAX];
const char *name = np->name;
@@ -159,12 +163,12 @@ static void __init of_at91sam9x5_clk_smd_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- clk = at91sam9x5_clk_register_smd(regmap, name, parent_names,
+ hw = at91sam9x5_clk_register_smd(regmap, name, parent_names,
num_parents);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd",
of_at91sam9x5_clk_smd_setup);
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
index 8f35d81..86a3680 100644
--- a/drivers/clk/at91/clk-system.c
+++ b/drivers/clk/at91/clk-system.c
@@ -88,13 +88,14 @@ static const struct clk_ops system_ops = {
.is_prepared = clk_system_is_prepared,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_system(struct regmap *regmap, const char *name,
const char *parent_name, u8 id)
{
struct clk_system *sys;
- struct clk *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
if (!parent_name || id > SYSTEM_MAX_ID)
return ERR_PTR(-EINVAL);
@@ -113,18 +114,21 @@ at91_clk_register_system(struct regmap *regmap, const char *name,
sys->hw.init = &init;
sys->regmap = regmap;
- clk = clk_register(NULL, &sys->hw);
- if (IS_ERR(clk))
+ hw = &sys->hw;
+ ret = clk_hw_register(NULL, &sys->hw);
+ if (ret) {
kfree(sys);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
{
int num;
u32 id;
- struct clk *clk;
+ struct clk_hw *hw;
const char *name;
struct device_node *sysclknp;
const char *parent_name;
@@ -147,11 +151,11 @@ static void __init of_at91rm9200_clk_sys_setup(struct device_node *np)
parent_name = of_clk_get_parent_name(sysclknp, 0);
- clk = at91_clk_register_system(regmap, name, parent_name, id);
- if (IS_ERR(clk))
+ hw = at91_clk_register_system(regmap, name, parent_name, id);
+ if (IS_ERR(hw))
continue;
- of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(sysclknp, of_clk_hw_simple_get, hw);
}
}
CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system",
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index d80bdb0..791770a 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -192,13 +192,14 @@ 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)
{
struct at91sam9x5_clk_usb *usb;
- struct clk *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
if (!usb)
@@ -214,20 +215,24 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
usb->hw.init = &init;
usb->regmap = regmap;
- clk = clk_register(NULL, &usb->hw);
- if (IS_ERR(clk))
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
+ if (ret) {
kfree(usb);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
-static struct clk * __init
+static 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 *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
if (!usb)
@@ -242,11 +247,14 @@ at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
usb->hw.init = &init;
usb->regmap = regmap;
- clk = clk_register(NULL, &usb->hw);
- if (IS_ERR(clk))
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
+ if (ret) {
kfree(usb);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk_hw *hw,
@@ -334,13 +342,14 @@ static const struct clk_ops at91rm9200_usb_ops = {
.set_rate = at91rm9200_clk_usb_set_rate,
};
-static struct clk * __init
+static 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 *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
if (!usb)
@@ -356,16 +365,19 @@ at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
usb->regmap = regmap;
memcpy(usb->divisors, divisors, sizeof(usb->divisors));
- clk = clk_register(NULL, &usb->hw);
- if (IS_ERR(clk))
+ hw = &usb->hw;
+ ret = clk_hw_register(NULL, &usb->hw);
+ if (ret) {
kfree(usb);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
unsigned int num_parents;
const char *parent_names[USB_SOURCE_MAX];
const char *name = np->name;
@@ -383,19 +395,19 @@ static void __init of_at91sam9x5_clk_usb_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- clk = at91sam9x5_clk_register_usb(regmap, name, parent_names,
- num_parents);
- if (IS_ERR(clk))
+ hw = at91sam9x5_clk_register_usb(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb",
of_at91sam9x5_clk_usb_setup);
static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
const char *parent_name;
const char *name = np->name;
struct regmap *regmap;
@@ -410,18 +422,18 @@ static void __init of_at91sam9n12_clk_usb_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- clk = at91sam9n12_clk_register_usb(regmap, name, parent_name);
- if (IS_ERR(clk))
+ hw = at91sam9n12_clk_register_usb(regmap, name, parent_name);
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb",
of_at91sam9n12_clk_usb_setup);
static void __init of_at91rm9200_clk_usb_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
const char *parent_name;
const char *name = np->name;
u32 divisors[4] = {0, 0, 0, 0};
@@ -440,12 +452,11 @@ static void __init of_at91rm9200_clk_usb_setup(struct device_node *np)
regmap = syscon_node_to_regmap(of_get_parent(np));
if (IS_ERR(regmap))
return;
-
- clk = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors);
- if (IS_ERR(clk))
+ hw = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors);
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb",
of_at91rm9200_clk_usb_setup);
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index 61fcf39..aadabd9 100644
--- a/drivers/clk/at91/clk-utmi.c
+++ b/drivers/clk/at91/clk-utmi.c
@@ -77,13 +77,14 @@ static const struct clk_ops utmi_ops = {
.recalc_rate = clk_utmi_recalc_rate,
};
-static struct clk * __init
+static struct clk_hw * __init
at91_clk_register_utmi(struct regmap *regmap,
const char *name, const char *parent_name)
{
struct clk_utmi *utmi;
- struct clk *clk = NULL;
+ struct clk_hw *hw;
struct clk_init_data init;
+ int ret;
utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
if (!utmi)
@@ -98,16 +99,19 @@ at91_clk_register_utmi(struct regmap *regmap,
utmi->hw.init = &init;
utmi->regmap = regmap;
- clk = clk_register(NULL, &utmi->hw);
- if (IS_ERR(clk))
+ hw = &utmi->hw;
+ ret = clk_hw_register(NULL, &utmi->hw);
+ if (ret) {
kfree(utmi);
+ hw = ERR_PTR(ret);
+ }
- return clk;
+ return hw;
}
static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
const char *parent_name;
const char *name = np->name;
struct regmap *regmap;
@@ -120,11 +124,11 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
if (IS_ERR(regmap))
return;
- clk = at91_clk_register_utmi(regmap, name, parent_name);
- if (IS_ERR(clk))
+ hw = at91_clk_register_utmi(regmap, name, parent_name);
+ if (IS_ERR(hw))
return;
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
return;
}
CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi",
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index 1184d76..ab6ecef 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -12,11 +12,382 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
+#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
-#include "sckc.h"
+#define SLOW_CLOCK_FREQ 32768
+#define SLOWCK_SW_CYCLES 5
+#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
+ SLOW_CLOCK_FREQ)
+
+#define AT91_SCKC_CR 0x00
+#define AT91_SCKC_RCEN (1 << 0)
+#define AT91_SCKC_OSC32EN (1 << 1)
+#define AT91_SCKC_OSC32BYP (1 << 2)
+#define AT91_SCKC_OSCSEL (1 << 3)
+
+struct clk_slow_osc {
+ struct clk_hw hw;
+ void __iomem *sckcr;
+ unsigned long startup_usec;
+};
+
+#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
+
+struct clk_sama5d4_slow_osc {
+ struct clk_hw hw;
+ void __iomem *sckcr;
+ unsigned long startup_usec;
+ bool prepared;
+};
+
+#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;
+ unsigned long frequency;
+ unsigned long accuracy;
+ unsigned long startup_usec;
+};
+
+#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;
+ u8 parent;
+};
+
+#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, 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;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN))
+ return 0;
+
+ writel(tmp | AT91_SCKC_OSC32EN, sckcr);
+
+ usleep_range(osc->startup_usec, osc->startup_usec + 1);
+
+ return 0;
+}
+
+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;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & AT91_SCKC_OSC32BYP)
+ return;
+
+ writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
+}
+
+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;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & AT91_SCKC_OSC32BYP)
+ return 1;
+
+ return !!(tmp & AT91_SCKC_OSC32EN);
+}
+
+static const struct clk_ops slow_osc_ops = {
+ .prepare = clk_slow_osc_prepare,
+ .unprepare = clk_slow_osc_unprepare,
+ .is_prepared = clk_slow_osc_is_prepared,
+};
+
+static struct clk_hw * __init
+at91_clk_register_slow_osc(void __iomem *sckcr,
+ const char *name,
+ const char *parent_name,
+ unsigned long startup,
+ bool bypass)
+{
+ 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 = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
+
+ 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;
+
+ if (bypass)
+ writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
+ sckcr);
+
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
+ if (ret) {
+ kfree(osc);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+
+static void __init
+of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr)
+{
+ struct clk_hw *hw;
+ const char *parent_name;
+ const char *name = np->name;
+ u32 startup;
+ bool bypass;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ of_property_read_string(np, "clock-output-names", &name);
+ of_property_read_u32(np, "atmel,startup-time-usec", &startup);
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
+ bypass);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
+}
+
+static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
+
+ return osc->frequency;
+}
+
+static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
+ unsigned long parent_acc)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
+
+ return osc->accuracy;
+}
+
+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;
+
+ writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
+
+ usleep_range(osc->startup_usec, osc->startup_usec + 1);
+
+ return 0;
+}
+
+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;
+
+ writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
+}
+
+static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
+
+ return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
+}
+
+static const struct clk_ops slow_rc_osc_ops = {
+ .prepare = clk_slow_rc_osc_prepare,
+ .unprepare = clk_slow_rc_osc_unprepare,
+ .is_prepared = clk_slow_rc_osc_is_prepared,
+ .recalc_rate = clk_slow_rc_osc_recalc_rate,
+ .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
+};
+
+static struct clk_hw * __init
+at91_clk_register_slow_rc_osc(void __iomem *sckcr,
+ const char *name,
+ unsigned long frequency,
+ unsigned long accuracy,
+ unsigned long startup)
+{
+ 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 = 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->frequency = frequency;
+ osc->accuracy = accuracy;
+ osc->startup_usec = startup;
+
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
+ if (ret) {
+ kfree(osc);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+
+static void __init
+of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr)
+{
+ struct clk_hw *hw;
+ u32 frequency = 0;
+ u32 accuracy = 0;
+ u32 startup = 0;
+ const char *name = np->name;
+
+ of_property_read_string(np, "clock-output-names", &name);
+ of_property_read_u32(np, "clock-frequency", &frequency);
+ of_property_read_u32(np, "clock-accuracy", &accuracy);
+ of_property_read_u32(np, "atmel,startup-time-usec", &startup);
+
+ hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy,
+ startup);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
+}
+
+static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
+ void __iomem *sckcr = slowck->sckcr;
+ u32 tmp;
+
+ if (index > 1)
+ return -EINVAL;
+
+ tmp = readl(sckcr);
+
+ if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
+ (index && (tmp & AT91_SCKC_OSCSEL)))
+ return 0;
+
+ if (index)
+ tmp |= AT91_SCKC_OSCSEL;
+ else
+ tmp &= ~AT91_SCKC_OSCSEL;
+
+ writel(tmp, sckcr);
+
+ usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
+
+ return 0;
+}
+
+static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
+{
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
+
+ return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
+}
+
+static const struct clk_ops sam9x5_slow_ops = {
+ .set_parent = clk_sam9x5_slow_set_parent,
+ .get_parent = clk_sam9x5_slow_get_parent,
+};
+
+static struct clk_hw * __init
+at91_clk_register_sam9x5_slow(void __iomem *sckcr,
+ const char *name,
+ const char **parent_names,
+ int num_parents)
+{
+ 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 = 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;
+
+ slowck->hw.init = &init;
+ slowck->sckcr = sckcr;
+ slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
+
+ hw = &slowck->hw;
+ ret = clk_hw_register(NULL, &slowck->hw);
+ if (ret) {
+ kfree(slowck);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
+
+static void __init
+of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr)
+{
+ struct clk_hw *hw;
+ const char *parent_names[2];
+ unsigned int num_parents;
+ const char *name = np->name;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > 2)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
+ num_parents);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
+}
static const struct of_device_id sckc_clk_ids[] __initconst = {
/* Slow clock */
@@ -55,3 +426,94 @@ static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
}
CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
of_at91sam9x5_sckc_setup);
+
+static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
+{
+ struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
+
+ if (osc->prepared)
+ return 0;
+
+ /*
+ * Assume that if it has already been selected (for example by the
+ * bootloader), enough time has aready passed.
+ */
+ if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) {
+ osc->prepared = true;
+ return 0;
+ }
+
+ usleep_range(osc->startup_usec, osc->startup_usec + 1);
+ osc->prepared = true;
+
+ return 0;
+}
+
+static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
+{
+ struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
+
+ return osc->prepared;
+}
+
+static const struct clk_ops sama5d4_slow_osc_ops = {
+ .prepare = clk_sama5d4_slow_osc_prepare,
+ .is_prepared = clk_sama5d4_slow_osc_is_prepared,
+};
+
+static void __init of_sama5d4_sckc_setup(struct device_node *np)
+{
+ void __iomem *regbase = of_iomap(np, 0);
+ struct clk_hw *hw;
+ 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" };
+ bool bypass;
+ int ret;
+
+ if (!regbase)
+ return;
+
+ hw = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
+ NULL, 0, 32768,
+ 250000000);
+ if (IS_ERR(hw))
+ return;
+
+ xtal_name = of_clk_get_parent_name(np, 0);
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return;
+
+ 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;
+
+ if (bypass)
+ writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase);
+
+ hw = &osc->hw;
+ ret = clk_hw_register(NULL, &osc->hw);
+ if (ret) {
+ kfree(osc);
+ return;
+ }
+
+ hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
+ if (IS_ERR(hw))
+ return;
+
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
+}
+CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
+ of_sama5d4_sckc_setup);
diff --git a/drivers/clk/at91/sckc.h b/drivers/clk/at91/sckc.h
deleted file mode 100644
index 836fcf5..0000000
--- a/drivers/clk/at91/sckc.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * drivers/clk/at91/sckc.h
- *
- * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __AT91_SCKC_H_
-#define __AT91_SCKC_H_
-
-extern void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np,
- void __iomem *sckcr);
-extern void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np,
- void __iomem *sckcr);
-extern void __init of_at91sam9x5_clk_slow_setup(struct device_node *np,
- void __iomem *sckcr);
-
-#endif /* __AT91_SCKC_H_ */
diff --git a/drivers/clk/axis/clk-artpec6.c b/drivers/clk/axis/clk-artpec6.c
index ffc988b..da1a073 100644
--- a/drivers/clk/axis/clk-artpec6.c
+++ b/drivers/clk/axis/clk-artpec6.c
@@ -113,8 +113,8 @@ static void of_artpec6_clkctrl_setup(struct device_node *np)
of_clk_add_provider(np, of_clk_src_onecell_get, &clkdata->clk_data);
}
-CLK_OF_DECLARE(artpec6_clkctrl, "axis,artpec6-clkctrl",
- of_artpec6_clkctrl_setup);
+CLK_OF_DECLARE_DRIVER(artpec6_clkctrl, "axis,artpec6-clkctrl",
+ of_artpec6_clkctrl_setup);
static int artpec6_clkctrl_probe(struct platform_device *pdev)
{
diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig
index f287845..f21e9b7 100644
--- a/drivers/clk/bcm/Kconfig
+++ b/drivers/clk/bcm/Kconfig
@@ -19,8 +19,36 @@ config CLK_BCM_KONA
in the BCM281xx and BCM21664 families.
config COMMON_CLK_IPROC
- bool
+ bool "Broadcom iProc clock support"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
depends on COMMON_CLK
+ default ARCH_BCM_IPROC
help
Enable common clock framework support for Broadcom SoCs
based on the iProc architecture
+
+if COMMON_CLK_IPROC
+
+config CLK_BCM_CYGNUS
+ bool "Broadcom Cygnus clock support"
+ depends on ARCH_BCM_CYGNUS || COMPILE_TEST
+ default ARCH_BCM_CYGNUS
+ help
+ Enable common clock framework support for the Broadcom Cygnus SoC
+
+config CLK_BCM_NSP
+ bool "Broadcom Northstar/Northstar Plus clock support"
+ depends on ARCH_BCM_5301X || ARCH_BCM_NSP || COMPILE_TEST
+ default ARCH_BCM_5301X || ARCH_BCM_NSP
+ help
+ Enable common clock framework support for the Broadcom Northstar and
+ Northstar Plus SoCs
+
+config CLK_BCM_NS2
+ bool "Broadcom Northstar 2 clock support"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ default ARCH_BCM_IPROC
+ help
+ Enable common clock framework support for the Broadcom Northstar 2 SoC
+
+endif
diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile
index 1d79bd2..d9dc848 100644
--- a/drivers/clk/bcm/Makefile
+++ b/drivers/clk/bcm/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o
obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o
-obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o
-obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o
-obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o
-obj-$(CONFIG_ARCH_BCM_5301X) += clk-nsp.o
+obj-$(CONFIG_ARCH_BCM_53573) += clk-bcm53573-ilp.o
+obj-$(CONFIG_CLK_BCM_CYGNUS) += clk-cygnus.o
+obj-$(CONFIG_CLK_BCM_NSP) += clk-nsp.o
+obj-$(CONFIG_CLK_BCM_NS2) += clk-ns2.o
diff --git a/drivers/clk/bcm/clk-bcm2835-aux.c b/drivers/clk/bcm/clk-bcm2835-aux.c
index 3a177ad..bd750cf 100644
--- a/drivers/clk/bcm/clk-bcm2835-aux.c
+++ b/drivers/clk/bcm/clk-bcm2835-aux.c
@@ -25,7 +25,7 @@
static int bcm2835_aux_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct clk_onecell_data *onecell;
+ struct clk_hw_onecell_data *onecell;
const char *parent;
struct clk *parent_clk;
struct resource *res;
@@ -41,28 +41,24 @@ static int bcm2835_aux_clk_probe(struct platform_device *pdev)
if (IS_ERR(reg))
return PTR_ERR(reg);
- onecell = devm_kmalloc(dev, sizeof(*onecell), GFP_KERNEL);
+ onecell = devm_kmalloc(dev, sizeof(*onecell) + sizeof(*onecell->hws) *
+ BCM2835_AUX_CLOCK_COUNT, GFP_KERNEL);
if (!onecell)
return -ENOMEM;
- onecell->clk_num = BCM2835_AUX_CLOCK_COUNT;
- onecell->clks = devm_kcalloc(dev, BCM2835_AUX_CLOCK_COUNT,
- sizeof(*onecell->clks), GFP_KERNEL);
- if (!onecell->clks)
- return -ENOMEM;
+ onecell->num = BCM2835_AUX_CLOCK_COUNT;
gate = reg + BCM2835_AUXENB;
- onecell->clks[BCM2835_AUX_CLOCK_UART] =
- clk_register_gate(dev, "aux_uart", parent, 0, gate, 0, 0, NULL);
-
- onecell->clks[BCM2835_AUX_CLOCK_SPI1] =
- clk_register_gate(dev, "aux_spi1", parent, 0, gate, 1, 0, NULL);
+ onecell->hws[BCM2835_AUX_CLOCK_UART] =
+ clk_hw_register_gate(dev, "aux_uart", parent, 0, gate, 0, 0, NULL);
- onecell->clks[BCM2835_AUX_CLOCK_SPI2] =
- clk_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL);
+ onecell->hws[BCM2835_AUX_CLOCK_SPI1] =
+ clk_hw_register_gate(dev, "aux_spi1", parent, 0, gate, 1, 0, NULL);
- of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, onecell);
+ onecell->hws[BCM2835_AUX_CLOCK_SPI2] =
+ clk_hw_register_gate(dev, "aux_spi2", parent, 0, gate, 2, 0, NULL);
- return 0;
+ return of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
+ onecell);
}
static const struct of_device_id bcm2835_aux_clk_of_match[] = {
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c
index 7a79708..b68bf57 100644
--- a/drivers/clk/bcm/clk-bcm2835.c
+++ b/drivers/clk/bcm/clk-bcm2835.c
@@ -36,6 +36,7 @@
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
+#include <linux/clk.h>
#include <linux/clk/bcm2835.h>
#include <linux/debugfs.h>
#include <linux/module.h>
@@ -302,8 +303,8 @@ struct bcm2835_cprman {
spinlock_t regs_lock; /* spinlock for all clocks */
const char *osc_name;
- struct clk_onecell_data onecell;
- struct clk *clks[];
+ /* Must be last */
+ struct clk_hw_onecell_data onecell;
};
static inline void cprman_write(struct bcm2835_cprman *cprman, u32 reg, u32 val)
@@ -344,24 +345,24 @@ static int bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base,
*/
void __init bcm2835_init_clocks(void)
{
- struct clk *clk;
+ struct clk_hw *hw;
int ret;
- clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 126000000);
- if (IS_ERR(clk))
+ hw = clk_hw_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 126000000);
+ if (IS_ERR(hw))
pr_err("apb_pclk not registered\n");
- clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, 0, 3000000);
- if (IS_ERR(clk))
+ hw = clk_hw_register_fixed_rate(NULL, "uart0_pclk", NULL, 0, 3000000);
+ if (IS_ERR(hw))
pr_err("uart0_pclk not registered\n");
- ret = clk_register_clkdev(clk, NULL, "20201000.uart");
+ ret = clk_hw_register_clkdev(hw, NULL, "20201000.uart");
if (ret)
pr_err("uart0_pclk alias not registered\n");
- clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, 0, 125000000);
- if (IS_ERR(clk))
+ hw = clk_hw_register_fixed_rate(NULL, "uart1_pclk", NULL, 0, 125000000);
+ if (IS_ERR(hw))
pr_err("uart1_pclk not registered\n");
- ret = clk_register_clkdev(clk, NULL, "20215000.uart");
+ ret = clk_hw_register_clkdev(hw, NULL, "20215000.uart");
if (ret)
pr_err("uart1_pclk alias not registered\n");
}
@@ -443,6 +444,8 @@ struct bcm2835_clock_data {
/* Number of fractional bits in the divider */
u32 frac_bits;
+ u32 flags;
+
bool is_vpu_clock;
bool is_mash_clock;
};
@@ -1006,16 +1009,28 @@ static int bcm2835_clock_set_rate(struct clk_hw *hw,
return 0;
}
+static bool
+bcm2835_clk_is_pllc(struct clk_hw *hw)
+{
+ if (!hw)
+ return false;
+
+ return strncmp(clk_hw_get_name(hw), "pllc", 4) == 0;
+}
+
static int bcm2835_clock_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
struct clk_hw *parent, *best_parent = NULL;
+ bool current_parent_is_pllc;
unsigned long rate, best_rate = 0;
unsigned long prate, best_prate = 0;
size_t i;
u32 div;
+ current_parent_is_pllc = bcm2835_clk_is_pllc(clk_hw_get_parent(hw));
+
/*
* Select parent clock that results in the closest but lower rate
*/
@@ -1023,6 +1038,17 @@ static int bcm2835_clock_determine_rate(struct clk_hw *hw,
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
+
+ /*
+ * Don't choose a PLLC-derived clock as our parent
+ * unless it had been manually set that way. PLLC's
+ * frequency gets adjusted by the firmware due to
+ * over-temp or under-voltage conditions, without
+ * prior notification to our clock consumer.
+ */
+ if (bcm2835_clk_is_pllc(parent) && !current_parent_is_pllc)
+ continue;
+
prate = clk_hw_get_rate(parent);
div = bcm2835_clock_choose_div(hw, req->rate, prate, true);
rate = bcm2835_clock_rate_from_divisor(clock, prate, div);
@@ -1121,11 +1147,12 @@ static const struct clk_ops bcm2835_vpu_clock_clk_ops = {
.debug_init = bcm2835_clock_debug_init,
};
-static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman,
- const struct bcm2835_pll_data *data)
+static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
+ const struct bcm2835_pll_data *data)
{
struct bcm2835_pll *pll;
struct clk_init_data init;
+ int ret;
memset(&init, 0, sizeof(init));
@@ -1144,17 +1171,20 @@ static struct clk *bcm2835_register_pll(struct bcm2835_cprman *cprman,
pll->data = data;
pll->hw.init = &init;
- return devm_clk_register(cprman->dev, &pll->hw);
+ ret = devm_clk_hw_register(cprman->dev, &pll->hw);
+ if (ret)
+ return NULL;
+ return &pll->hw;
}
-static struct clk *
+static struct clk_hw *
bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
const struct bcm2835_pll_divider_data *data)
{
struct bcm2835_pll_divider *divider;
struct clk_init_data init;
- struct clk *clk;
const char *divider_name;
+ int ret;
if (data->fixed_divider != 1) {
divider_name = devm_kasprintf(cprman->dev, GFP_KERNEL,
@@ -1188,32 +1218,33 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
divider->cprman = cprman;
divider->data = data;
- clk = devm_clk_register(cprman->dev, &divider->div.hw);
- if (IS_ERR(clk))
- return clk;
+ ret = devm_clk_hw_register(cprman->dev, &divider->div.hw);
+ if (ret)
+ return ERR_PTR(ret);
/*
* PLLH's channels have a fixed divide by 10 afterwards, which
* is what our consumers are actually using.
*/
if (data->fixed_divider != 1) {
- return clk_register_fixed_factor(cprman->dev, data->name,
- divider_name,
- CLK_SET_RATE_PARENT,
- 1,
- data->fixed_divider);
+ return clk_hw_register_fixed_factor(cprman->dev, data->name,
+ divider_name,
+ CLK_SET_RATE_PARENT,
+ 1,
+ data->fixed_divider);
}
- return clk;
+ return &divider->div.hw;
}
-static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman,
+static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
const struct bcm2835_clock_data *data)
{
struct bcm2835_clock *clock;
struct clk_init_data init;
const char *parents[1 << CM_SRC_BITS];
size_t i;
+ int ret;
/*
* Replace our "xosc" references with the oscillator's
@@ -1230,13 +1261,19 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman,
init.parent_names = parents;
init.num_parents = data->num_mux_parents;
init.name = data->name;
- init.flags = CLK_IGNORE_UNUSED;
+ init.flags = data->flags | CLK_IGNORE_UNUSED;
if (data->is_vpu_clock) {
init.ops = &bcm2835_vpu_clock_clk_ops;
} else {
init.ops = &bcm2835_clock_clk_ops;
init.flags |= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+ /* If the clock wasn't actually enabled at boot, it's not
+ * critical.
+ */
+ if (!(cprman_read(cprman, data->ctl_reg) & CM_ENABLE))
+ init.flags &= ~CLK_IS_CRITICAL;
}
clock = devm_kzalloc(cprman->dev, sizeof(*clock), GFP_KERNEL);
@@ -1247,7 +1284,10 @@ static struct clk *bcm2835_register_clock(struct bcm2835_cprman *cprman,
clock->data = data;
clock->hw.init = &init;
- return devm_clk_register(cprman->dev, &clock->hw);
+ ret = devm_clk_hw_register(cprman->dev, &clock->hw);
+ if (ret)
+ return ERR_PTR(ret);
+ return &clock->hw;
}
static struct clk *bcm2835_register_gate(struct bcm2835_cprman *cprman,
@@ -1259,8 +1299,8 @@ static struct clk *bcm2835_register_gate(struct bcm2835_cprman *cprman,
CM_GATE_BIT, 0, &cprman->regs_lock);
}
-typedef struct clk *(*bcm2835_clk_register)(struct bcm2835_cprman *cprman,
- const void *data);
+typedef struct clk_hw *(*bcm2835_clk_register)(struct bcm2835_cprman *cprman,
+ const void *data);
struct bcm2835_clk_desc {
bcm2835_clk_register clk_register;
const void *data;
@@ -1649,6 +1689,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.div_reg = CM_VPUDIV,
.int_bits = 12,
.frac_bits = 8,
+ .flags = CLK_IS_CRITICAL,
.is_vpu_clock = true),
/* clocks with per parent mux */
@@ -1705,13 +1746,15 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.div_reg = CM_GP1DIV,
.int_bits = 12,
.frac_bits = 12,
+ .flags = CLK_IS_CRITICAL,
.is_mash_clock = true),
[BCM2835_CLOCK_GP2] = REGISTER_PER_CLK(
.name = "gp2",
.ctl_reg = CM_GP2CTL,
.div_reg = CM_GP2DIV,
.int_bits = 12,
- .frac_bits = 12),
+ .frac_bits = 12,
+ .flags = CLK_IS_CRITICAL),
/* HDMI state machine */
[BCM2835_CLOCK_HSM] = REGISTER_PER_CLK(
@@ -1790,18 +1833,38 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.ctl_reg = CM_PERIICTL),
};
+/*
+ * Permanently take a reference on the parent of the SDRAM clock.
+ *
+ * While the SDRAM is being driven by its dedicated PLL most of the
+ * time, there is a little loop running in the firmware that
+ * periodically switches the SDRAM to using our CM clock to do PVT
+ * recalibration, with the assumption that the previously configured
+ * SDRAM parent is still enabled and running.
+ */
+static int bcm2835_mark_sdc_parent_critical(struct clk *sdc)
+{
+ struct clk *parent = clk_get_parent(sdc);
+
+ if (IS_ERR(parent))
+ return PTR_ERR(parent);
+
+ return clk_prepare_enable(parent);
+}
+
static int bcm2835_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct clk **clks;
+ struct clk_hw **hws;
struct bcm2835_cprman *cprman;
struct resource *res;
const struct bcm2835_clk_desc *desc;
const size_t asize = ARRAY_SIZE(clk_desc_array);
size_t i;
+ int ret;
- cprman = devm_kzalloc(dev,
- sizeof(*cprman) + asize * sizeof(*clks),
+ cprman = devm_kzalloc(dev, sizeof(*cprman) +
+ sizeof(*cprman->onecell.hws) * asize,
GFP_KERNEL);
if (!cprman)
return -ENOMEM;
@@ -1819,18 +1882,21 @@ static int bcm2835_clk_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, cprman);
- cprman->onecell.clk_num = asize;
- cprman->onecell.clks = cprman->clks;
- clks = cprman->clks;
+ cprman->onecell.num = asize;
+ hws = cprman->onecell.hws;
for (i = 0; i < asize; i++) {
desc = &clk_desc_array[i];
if (desc->clk_register && desc->data)
- clks[i] = desc->clk_register(cprman, desc->data);
+ hws[i] = desc->clk_register(cprman, desc->data);
}
- return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
- &cprman->onecell);
+ ret = bcm2835_mark_sdc_parent_critical(hws[BCM2835_CLOCK_SDRAM]->clk);
+ if (ret)
+ return ret;
+
+ return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+ &cprman->onecell);
}
static const struct of_device_id bcm2835_clk_of_match[] = {
diff --git a/drivers/clk/bcm/clk-bcm53573-ilp.c b/drivers/clk/bcm/clk-bcm53573-ilp.c
new file mode 100644
index 0000000..36eb371
--- /dev/null
+++ b/drivers/clk/bcm/clk-bcm53573-ilp.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define PMU_XTAL_FREQ_RATIO 0x66c
+#define XTAL_ALP_PER_4ILP 0x00001fff
+#define XTAL_CTL_EN 0x80000000
+#define PMU_SLOW_CLK_PERIOD 0x6dc
+
+struct bcm53573_ilp {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+static int bcm53573_ilp_enable(struct clk_hw *hw)
+{
+ struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
+
+ regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0x10199);
+ regmap_write(ilp->regmap, 0x674, 0x10000);
+
+ return 0;
+}
+
+static void bcm53573_ilp_disable(struct clk_hw *hw)
+{
+ struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
+
+ regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0);
+ regmap_write(ilp->regmap, 0x674, 0);
+}
+
+static unsigned long bcm53573_ilp_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);
+ struct regmap *regmap = ilp->regmap;
+ u32 last_val, cur_val;
+ int sum = 0, num = 0, loop_num = 0;
+ int avg;
+
+ /* Enable measurement */
+ regmap_write(regmap, PMU_XTAL_FREQ_RATIO, XTAL_CTL_EN);
+
+ /* Read initial value */
+ regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &last_val);
+ last_val &= XTAL_ALP_PER_4ILP;
+
+ /*
+ * At minimum we should loop for a bit to let hardware do the
+ * measurement. This isn't very accurate however, so for a better
+ * precision lets try getting 20 different values for and use average.
+ */
+ while (num < 20) {
+ regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &cur_val);
+ cur_val &= XTAL_ALP_PER_4ILP;
+
+ if (cur_val != last_val) {
+ /* Got different value, use it */
+ sum += cur_val;
+ num++;
+ loop_num = 0;
+ last_val = cur_val;
+ } else if (++loop_num > 5000) {
+ /* Same value over and over, give up */
+ sum += cur_val;
+ num++;
+ break;
+ }
+
+ cpu_relax();
+ }
+
+ /* Disable measurement to save power */
+ regmap_write(regmap, PMU_XTAL_FREQ_RATIO, 0x0);
+
+ avg = sum / num;
+
+ return parent_rate * 4 / avg;
+}
+
+static const struct clk_ops bcm53573_ilp_clk_ops = {
+ .enable = bcm53573_ilp_enable,
+ .disable = bcm53573_ilp_disable,
+ .recalc_rate = bcm53573_ilp_recalc_rate,
+};
+
+static void bcm53573_ilp_init(struct device_node *np)
+{
+ struct bcm53573_ilp *ilp;
+ struct clk_init_data init = { };
+ const char *parent_name;
+ int err;
+
+ ilp = kzalloc(sizeof(*ilp), GFP_KERNEL);
+ if (!ilp)
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name) {
+ err = -ENOENT;
+ goto err_free_ilp;
+ }
+
+ ilp->regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(ilp->regmap)) {
+ err = PTR_ERR(ilp->regmap);
+ goto err_free_ilp;
+ }
+
+ init.name = np->name;
+ init.ops = &bcm53573_ilp_clk_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ ilp->hw.init = &init;
+ err = clk_hw_register(NULL, &ilp->hw);
+ if (err)
+ goto err_free_ilp;
+
+ err = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &ilp->hw);
+ if (err)
+ goto err_clk_hw_unregister;
+
+ return;
+
+err_clk_hw_unregister:
+ clk_hw_unregister(&ilp->hw);
+err_free_ilp:
+ kfree(ilp);
+ pr_err("Failed to init ILP clock: %d\n", err);
+}
+
+/* We need it very early for arch code, before device model gets ready */
+CLK_OF_DECLARE(bcm53573_ilp_clk, "brcm,bcm53573-ilp", bcm53573_ilp_init);
diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c
index 526b0b0..c37a7f0 100644
--- a/drivers/clk/bcm/clk-kona-setup.c
+++ b/drivers/clk/bcm/clk-kona-setup.c
@@ -586,8 +586,8 @@ static u32 *parent_process(const char *clocks[],
}
/* There is at least one parent, so allocate a selector array */
-
- parent_sel = kmalloc(parent_count * sizeof(*parent_sel), GFP_KERNEL);
+ parent_sel = kmalloc_array(parent_count, sizeof(*parent_sel),
+ GFP_KERNEL);
if (!parent_sel) {
pr_err("%s: error allocating %u parent selectors\n", __func__,
parent_count);
@@ -696,77 +696,69 @@ static void bcm_clk_teardown(struct kona_clk *bcm_clk)
bcm_clk->type = bcm_clk_none;
}
-static void kona_clk_teardown(struct clk *clk)
+static void kona_clk_teardown(struct clk_hw *hw)
{
- struct clk_hw *hw;
struct kona_clk *bcm_clk;
- if (!clk)
+ if (!hw)
return;
- hw = __clk_get_hw(clk);
- if (!hw) {
- pr_err("%s: clk %p has null hw pointer\n", __func__, clk);
- return;
- }
- clk_unregister(clk);
+ clk_hw_unregister(hw);
bcm_clk = to_kona_clk(hw);
bcm_clk_teardown(bcm_clk);
}
-struct clk *kona_clk_setup(struct kona_clk *bcm_clk)
+static int kona_clk_setup(struct kona_clk *bcm_clk)
{
+ int ret;
struct clk_init_data *init_data = &bcm_clk->init_data;
- struct clk *clk = NULL;
switch (bcm_clk->type) {
case bcm_clk_peri:
- if (peri_clk_setup(bcm_clk->u.data, init_data))
- return NULL;
+ ret = peri_clk_setup(bcm_clk->u.data, init_data);
+ if (ret)
+ return ret;
break;
default:
pr_err("%s: clock type %d invalid for %s\n", __func__,
(int)bcm_clk->type, init_data->name);
- return NULL;
+ return -EINVAL;
}
/* Make sure everything makes sense before we set it up */
if (!kona_clk_valid(bcm_clk)) {
pr_err("%s: clock data invalid for %s\n", __func__,
init_data->name);
+ ret = -EINVAL;
goto out_teardown;
}
bcm_clk->hw.init = init_data;
- clk = clk_register(NULL, &bcm_clk->hw);
- if (IS_ERR(clk)) {
- pr_err("%s: error registering clock %s (%ld)\n", __func__,
- init_data->name, PTR_ERR(clk));
+ ret = clk_hw_register(NULL, &bcm_clk->hw);
+ if (ret) {
+ pr_err("%s: error registering clock %s (%d)\n", __func__,
+ init_data->name, ret);
goto out_teardown;
}
- BUG_ON(!clk);
- return clk;
+ return 0;
out_teardown:
bcm_clk_teardown(bcm_clk);
- return NULL;
+ return ret;
}
static void ccu_clks_teardown(struct ccu_data *ccu)
{
u32 i;
- for (i = 0; i < ccu->clk_data.clk_num; i++)
- kona_clk_teardown(ccu->clk_data.clks[i]);
- kfree(ccu->clk_data.clks);
+ for (i = 0; i < ccu->clk_num; i++)
+ kona_clk_teardown(&ccu->kona_clks[i].hw);
}
static void kona_ccu_teardown(struct ccu_data *ccu)
{
- kfree(ccu->clk_data.clks);
- ccu->clk_data.clks = NULL;
if (!ccu->base)
return;
@@ -793,6 +785,20 @@ static bool ccu_data_valid(struct ccu_data *ccu)
return true;
}
+static struct clk_hw *
+of_clk_kona_onecell_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct ccu_data *ccu = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= ccu->clk_num) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &ccu->kona_clks[idx].hw;
+}
+
/*
* Set up a CCU. Call the provided ccu_clks_setup callback to
* initialize the array of clocks provided by the CCU.
@@ -805,18 +811,6 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu,
unsigned int i;
int ret;
- if (ccu->clk_data.clk_num) {
- size_t size;
-
- size = ccu->clk_data.clk_num * sizeof(*ccu->clk_data.clks);
- ccu->clk_data.clks = kzalloc(size, GFP_KERNEL);
- if (!ccu->clk_data.clks) {
- pr_err("%s: unable to allocate %u clocks for %s\n",
- __func__, ccu->clk_data.clk_num, node->name);
- return;
- }
- }
-
ret = of_address_to_resource(node, 0, &res);
if (ret) {
pr_err("%s: no valid CCU registers found for %s\n", __func__,
@@ -851,13 +845,13 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu,
* the clock framework clock array (in ccu->data). Then
* register as a provider for these clocks.
*/
- for (i = 0; i < ccu->clk_data.clk_num; i++) {
+ for (i = 0; i < ccu->clk_num; i++) {
if (!ccu->kona_clks[i].ccu)
continue;
- ccu->clk_data.clks[i] = kona_clk_setup(&ccu->kona_clks[i]);
+ kona_clk_setup(&ccu->kona_clks[i]);
}
- ret = of_clk_add_provider(node, of_clk_src_onecell_get, &ccu->clk_data);
+ ret = of_clk_add_hw_provider(node, of_clk_kona_onecell_get, ccu);
if (ret) {
pr_err("%s: error adding ccu %s as provider (%d)\n", __func__,
node->name, ret);
diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c
index 3a15347..eee64b9 100644
--- a/drivers/clk/bcm/clk-kona.c
+++ b/drivers/clk/bcm/clk-kona.c
@@ -1256,19 +1256,18 @@ bool __init kona_ccu_init(struct ccu_data *ccu)
{
unsigned long flags;
unsigned int which;
- struct clk **clks = ccu->clk_data.clks;
struct kona_clk *kona_clks = ccu->kona_clks;
bool success = true;
flags = ccu_lock(ccu);
__ccu_write_enable(ccu);
- for (which = 0; which < ccu->clk_data.clk_num; which++) {
- struct kona_clk *bcm_clk;
+ for (which = 0; which < ccu->clk_num; which++) {
+ struct kona_clk *bcm_clk = &kona_clks[which];
- if (!clks[which])
+ if (!bcm_clk->ccu)
continue;
- bcm_clk = &kona_clks[which];
+
success &= __kona_clk_init(bcm_clk);
}
diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h
index 906576e..f4b39bb 100644
--- a/drivers/clk/bcm/clk-kona.h
+++ b/drivers/clk/bcm/clk-kona.h
@@ -481,7 +481,7 @@ struct ccu_data {
bool write_enabled; /* write access is currently enabled */
struct ccu_policy policy;
struct device_node *node;
- struct clk_onecell_data clk_data;
+ size_t clk_num;
const char *name;
u32 range; /* byte range of address space */
struct kona_clk kona_clks[]; /* must be last */
@@ -491,9 +491,7 @@ struct ccu_data {
#define KONA_CCU_COMMON(_prefix, _name, _ccuname) \
.name = #_name "_ccu", \
.lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \
- .clk_data = { \
- .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \
- }
+ .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT
/* Exported globals */
@@ -505,7 +503,6 @@ extern u64 scaled_div_max(struct bcm_clk_div *div);
extern u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value,
u32 billionths);
-extern struct clk *kona_clk_setup(struct kona_clk *bcm_clk);
extern void __init kona_dt_ccu_setup(struct ccu_data *ccu,
struct device_node *node);
extern bool __init kona_ccu_init(struct ccu_data *ccu);
diff --git a/drivers/clk/berlin/berlin2-avpll.c b/drivers/clk/berlin/berlin2-avpll.c
index fd0f26c..cfcae46 100644
--- a/drivers/clk/berlin/berlin2-avpll.c
+++ b/drivers/clk/berlin/berlin2-avpll.c
@@ -188,7 +188,7 @@ static const struct clk_ops berlin2_avpll_vco_ops = {
.recalc_rate = berlin2_avpll_vco_recalc_rate,
};
-struct clk * __init berlin2_avpll_vco_register(void __iomem *base,
+int __init berlin2_avpll_vco_register(void __iomem *base,
const char *name, const char *parent_name,
u8 vco_flags, unsigned long flags)
{
@@ -197,7 +197,7 @@ struct clk * __init berlin2_avpll_vco_register(void __iomem *base,
vco = kzalloc(sizeof(*vco), GFP_KERNEL);
if (!vco)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
vco->base = base;
vco->flags = vco_flags;
@@ -208,7 +208,7 @@ struct clk * __init berlin2_avpll_vco_register(void __iomem *base,
init.num_parents = 1;
init.flags = flags;
- return clk_register(NULL, &vco->hw);
+ return clk_hw_register(NULL, &vco->hw);
}
struct berlin2_avpll_channel {
@@ -364,7 +364,7 @@ static const struct clk_ops berlin2_avpll_channel_ops = {
*/
static const u8 quirk_index[] __initconst = { 0, 6, 5, 4, 3, 2, 1, 7 };
-struct clk * __init berlin2_avpll_channel_register(void __iomem *base,
+int __init berlin2_avpll_channel_register(void __iomem *base,
const char *name, u8 index, const char *parent_name,
u8 ch_flags, unsigned long flags)
{
@@ -373,7 +373,7 @@ struct clk * __init berlin2_avpll_channel_register(void __iomem *base,
ch = kzalloc(sizeof(*ch), GFP_KERNEL);
if (!ch)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
ch->base = base;
if (ch_flags & BERLIN2_AVPLL_SCRAMBLE_QUIRK)
@@ -389,5 +389,5 @@ struct clk * __init berlin2_avpll_channel_register(void __iomem *base,
init.num_parents = 1;
init.flags = flags;
- return clk_register(NULL, &ch->hw);
+ return clk_hw_register(NULL, &ch->hw);
}
diff --git a/drivers/clk/berlin/berlin2-avpll.h b/drivers/clk/berlin/berlin2-avpll.h
index a37f506..17e3111 100644
--- a/drivers/clk/berlin/berlin2-avpll.h
+++ b/drivers/clk/berlin/berlin2-avpll.h
@@ -19,17 +19,13 @@
#ifndef __BERLIN2_AVPLL_H
#define __BERLIN2_AVPLL_H
-struct clk;
-
#define BERLIN2_AVPLL_BIT_QUIRK BIT(0)
#define BERLIN2_AVPLL_SCRAMBLE_QUIRK BIT(1)
-struct clk * __init
-berlin2_avpll_vco_register(void __iomem *base, const char *name,
+int berlin2_avpll_vco_register(void __iomem *base, const char *name,
const char *parent_name, u8 vco_flags, unsigned long flags);
-struct clk * __init
-berlin2_avpll_channel_register(void __iomem *base, const char *name,
+int berlin2_avpll_channel_register(void __iomem *base, const char *name,
u8 index, const char *parent_name, u8 ch_flags,
unsigned long flags);
diff --git a/drivers/clk/berlin/berlin2-div.c b/drivers/clk/berlin/berlin2-div.c
index 81ff97f..41ab2d3 100644
--- a/drivers/clk/berlin/berlin2-div.c
+++ b/drivers/clk/berlin/berlin2-div.c
@@ -234,7 +234,7 @@ static const struct clk_ops berlin2_div_mux_ops = {
.get_parent = berlin2_div_get_parent,
};
-struct clk * __init
+struct clk_hw * __init
berlin2_div_register(const struct berlin2_div_map *map,
void __iomem *base, const char *name, u8 div_flags,
const char **parent_names, int num_parents,
@@ -259,7 +259,7 @@ berlin2_div_register(const struct berlin2_div_map *map,
if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
mux_ops = NULL;
- return clk_register_composite(NULL, name, parent_names, num_parents,
+ return clk_hw_register_composite(NULL, name, parent_names, num_parents,
&div->hw, mux_ops, &div->hw, rate_ops,
&div->hw, gate_ops, flags);
}
diff --git a/drivers/clk/berlin/berlin2-div.h b/drivers/clk/berlin/berlin2-div.h
index 15e3384..e835ddf 100644
--- a/drivers/clk/berlin/berlin2-div.h
+++ b/drivers/clk/berlin/berlin2-div.h
@@ -19,7 +19,7 @@
#ifndef __BERLIN2_DIV_H
#define __BERLIN2_DIV_H
-struct clk;
+struct clk_hw;
#define BERLIN2_DIV_HAS_GATE BIT(0)
#define BERLIN2_DIV_HAS_MUX BIT(1)
@@ -80,7 +80,7 @@ struct berlin2_div_data {
u8 div_flags;
};
-struct clk * __init
+struct clk_hw *
berlin2_div_register(const struct berlin2_div_map *map,
void __iomem *base, const char *name, u8 div_flags,
const char **parent_names, int num_parents,
diff --git a/drivers/clk/berlin/berlin2-pll.c b/drivers/clk/berlin/berlin2-pll.c
index 1c2294d..4ffbe80 100644
--- a/drivers/clk/berlin/berlin2-pll.c
+++ b/drivers/clk/berlin/berlin2-pll.c
@@ -84,7 +84,7 @@ static const struct clk_ops berlin2_pll_ops = {
.recalc_rate = berlin2_pll_recalc_rate,
};
-struct clk * __init
+int __init
berlin2_pll_register(const struct berlin2_pll_map *map,
void __iomem *base, const char *name,
const char *parent_name, unsigned long flags)
@@ -94,7 +94,7 @@ berlin2_pll_register(const struct berlin2_pll_map *map,
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
/* copy pll_map to allow __initconst */
memcpy(&pll->map, map, sizeof(*map));
@@ -106,5 +106,5 @@ berlin2_pll_register(const struct berlin2_pll_map *map,
init.num_parents = 1;
init.flags = flags;
- return clk_register(NULL, &pll->hw);
+ return clk_hw_register(NULL, &pll->hw);
}
diff --git a/drivers/clk/berlin/berlin2-pll.h b/drivers/clk/berlin/berlin2-pll.h
index 8831ce2..583e024 100644
--- a/drivers/clk/berlin/berlin2-pll.h
+++ b/drivers/clk/berlin/berlin2-pll.h
@@ -19,8 +19,6 @@
#ifndef __BERLIN2_PLL_H
#define __BERLIN2_PLL_H
-struct clk;
-
struct berlin2_pll_map {
const u8 vcodiv[16];
u8 mult;
@@ -29,9 +27,8 @@ struct berlin2_pll_map {
u8 divsel_shift;
};
-struct clk * __init
-berlin2_pll_register(const struct berlin2_pll_map *map,
- void __iomem *base, const char *name,
- const char *parent_name, unsigned long flags);
+int berlin2_pll_register(const struct berlin2_pll_map *map,
+ void __iomem *base, const char *name,
+ const char *parent_name, unsigned long flags);
#endif /* __BERLIN2_PLL_H */
diff --git a/drivers/clk/berlin/bg2.c b/drivers/clk/berlin/bg2.c
index 23e0e3b..edf3b96 100644
--- a/drivers/clk/berlin/bg2.c
+++ b/drivers/clk/berlin/bg2.c
@@ -92,8 +92,7 @@
*/
#define MAX_CLKS 41
-static struct clk *clks[MAX_CLKS];
-static struct clk_onecell_data clk_data;
+static struct clk_hw_onecell_data *clk_data;
static DEFINE_SPINLOCK(lock);
static void __iomem *gbase;
@@ -505,8 +504,17 @@ static void __init berlin2_clock_setup(struct device_node *np)
struct device_node *parent_np = of_get_parent(np);
const char *parent_names[9];
struct clk *clk;
+ struct clk_hw *hw;
+ struct clk_hw **hws;
u8 avpll_flags = 0;
- int n;
+ int n, ret;
+
+ clk_data = kzalloc(sizeof(*clk_data) +
+ sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL);
+ if (!clk_data)
+ return;
+ clk_data->num = MAX_CLKS;
+ hws = clk_data->hws;
gbase = of_iomap(parent_np, 0);
if (!gbase)
@@ -526,118 +534,118 @@ static void __init berlin2_clock_setup(struct device_node *np)
}
/* simple register PLLs */
- clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_SYSPLLCTL0,
+ ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_SYSPLLCTL0,
clk_names[SYSPLL], clk_names[REFCLK], 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2_fail;
- clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_MEMPLLCTL0,
+ ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_MEMPLLCTL0,
clk_names[MEMPLL], clk_names[REFCLK], 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2_fail;
- clk = berlin2_pll_register(&bg2_pll_map, gbase + REG_CPUPLLCTL0,
+ ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_CPUPLLCTL0,
clk_names[CPUPLL], clk_names[REFCLK], 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2_fail;
if (of_device_is_compatible(np, "marvell,berlin2-global-register"))
avpll_flags |= BERLIN2_AVPLL_SCRAMBLE_QUIRK;
/* audio/video VCOs */
- clk = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL0, "avpll_vcoA",
+ ret = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL0, "avpll_vcoA",
clk_names[REFCLK], avpll_flags, 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2_fail;
for (n = 0; n < 8; n++) {
- clk = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL0,
+ ret = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL0,
clk_names[AVPLL_A1 + n], n, "avpll_vcoA",
avpll_flags, 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2_fail;
}
- clk = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL31, "avpll_vcoB",
+ ret = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL31, "avpll_vcoB",
clk_names[REFCLK], BERLIN2_AVPLL_BIT_QUIRK |
avpll_flags, 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2_fail;
for (n = 0; n < 8; n++) {
- clk = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL31,
+ ret = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL31,
clk_names[AVPLL_B1 + n], n, "avpll_vcoB",
BERLIN2_AVPLL_BIT_QUIRK | avpll_flags, 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2_fail;
}
/* reference clock bypass switches */
parent_names[0] = clk_names[SYSPLL];
parent_names[1] = clk_names[REFCLK];
- clk = clk_register_mux(NULL, "syspll_byp", parent_names, 2,
+ hw = clk_hw_register_mux(NULL, "syspll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 0, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
- clk_names[SYSPLL] = __clk_get_name(clk);
+ clk_names[SYSPLL] = clk_hw_get_name(hw);
parent_names[0] = clk_names[MEMPLL];
parent_names[1] = clk_names[REFCLK];
- clk = clk_register_mux(NULL, "mempll_byp", parent_names, 2,
+ hw = clk_hw_register_mux(NULL, "mempll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 1, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
- clk_names[MEMPLL] = __clk_get_name(clk);
+ clk_names[MEMPLL] = clk_hw_get_name(hw);
parent_names[0] = clk_names[CPUPLL];
parent_names[1] = clk_names[REFCLK];
- clk = clk_register_mux(NULL, "cpupll_byp", parent_names, 2,
+ hw = clk_hw_register_mux(NULL, "cpupll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 2, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
- clk_names[CPUPLL] = __clk_get_name(clk);
+ clk_names[CPUPLL] = clk_hw_get_name(hw);
/* clock muxes */
parent_names[0] = clk_names[AVPLL_B3];
parent_names[1] = clk_names[AVPLL_A3];
- clk = clk_register_mux(NULL, clk_names[AUDIO1_PLL], parent_names, 2,
+ hw = clk_hw_register_mux(NULL, clk_names[AUDIO1_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT2, 29, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO0_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
- clk = clk_register_mux(NULL, clk_names[VIDEO0_IN], parent_names, 2,
+ hw = clk_hw_register_mux(NULL, clk_names[VIDEO0_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 4, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO1_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
- clk = clk_register_mux(NULL, clk_names[VIDEO1_IN], parent_names, 2,
+ hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 6, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[AVPLL_A2];
parent_names[1] = clk_names[AVPLL_B2];
- clk = clk_register_mux(NULL, clk_names[VIDEO1_PLL], parent_names, 2,
+ hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT3, 7, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO2_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
- clk = clk_register_mux(NULL, clk_names[VIDEO2_IN], parent_names, 2,
+ hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 9, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[AVPLL_B1];
parent_names[1] = clk_names[AVPLL_A5];
- clk = clk_register_mux(NULL, clk_names[VIDEO2_PLL], parent_names, 2,
+ hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT3, 10, 1, 0, &lock);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
goto bg2_fail;
/* clock divider cells */
@@ -648,7 +656,7 @@ static void __init berlin2_clock_setup(struct device_node *np)
for (k = 0; k < dd->num_parents; k++)
parent_names[k] = clk_names[dd->parent_ids[k]];
- clks[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
+ hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
dd->name, dd->div_flags, parent_names,
dd->num_parents, dd->flags, &lock);
}
@@ -657,18 +665,18 @@ static void __init berlin2_clock_setup(struct device_node *np)
for (n = 0; n < ARRAY_SIZE(bg2_gates); n++) {
const struct berlin2_gate_data *gd = &bg2_gates[n];
- clks[CLKID_GETH0 + n] = clk_register_gate(NULL, gd->name,
+ hws[CLKID_GETH0 + n] = clk_hw_register_gate(NULL, gd->name,
gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
gd->bit_idx, 0, &lock);
}
/* twdclk is derived from cpu/3 */
- clks[CLKID_TWD] =
- clk_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
+ hws[CLKID_TWD] =
+ clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
- if (!IS_ERR(clks[n]))
+ if (!IS_ERR(hws[n]))
continue;
pr_err("%s: Unable to register leaf clock %d\n",
@@ -677,9 +685,7 @@ static void __init berlin2_clock_setup(struct device_node *np)
}
/* register clk-provider */
- clk_data.clks = clks;
- clk_data.clk_num = MAX_CLKS;
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data);
return;
diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c
index f144547..0718e83 100644
--- a/drivers/clk/berlin/bg2q.c
+++ b/drivers/clk/berlin/bg2q.c
@@ -46,8 +46,7 @@
#define REG_SDIO1XIN_CLKCTL 0x015c
#define MAX_CLKS 28
-static struct clk *clks[MAX_CLKS];
-static struct clk_onecell_data clk_data;
+static struct clk_hw_onecell_data *clk_data;
static DEFINE_SPINLOCK(lock);
static void __iomem *gbase;
static void __iomem *cpupll_base;
@@ -293,7 +292,15 @@ static void __init berlin2q_clock_setup(struct device_node *np)
struct device_node *parent_np = of_get_parent(np);
const char *parent_names[9];
struct clk *clk;
- int n;
+ struct clk_hw **hws;
+ int n, ret;
+
+ clk_data = kzalloc(sizeof(*clk_data) +
+ sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL);
+ if (!clk_data)
+ return;
+ clk_data->num = MAX_CLKS;
+ hws = clk_data->hws;
gbase = of_iomap(parent_np, 0);
if (!gbase) {
@@ -317,14 +324,14 @@ static void __init berlin2q_clock_setup(struct device_node *np)
}
/* simple register PLLs */
- clk = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0,
+ ret = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0,
clk_names[SYSPLL], clk_names[REFCLK], 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2q_fail;
- clk = berlin2_pll_register(&bg2q_pll_map, cpupll_base,
+ ret = berlin2_pll_register(&bg2q_pll_map, cpupll_base,
clk_names[CPUPLL], clk_names[REFCLK], 0);
- if (IS_ERR(clk))
+ if (ret)
goto bg2q_fail;
/* TODO: add BG2Q AVPLL */
@@ -342,7 +349,7 @@ static void __init berlin2q_clock_setup(struct device_node *np)
for (k = 0; k < dd->num_parents; k++)
parent_names[k] = clk_names[dd->parent_ids[k]];
- clks[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
+ hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
dd->name, dd->div_flags, parent_names,
dd->num_parents, dd->flags, &lock);
}
@@ -351,22 +358,22 @@ static void __init berlin2q_clock_setup(struct device_node *np)
for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) {
const struct berlin2_gate_data *gd = &bg2q_gates[n];
- clks[CLKID_GFX2DAXI + n] = clk_register_gate(NULL, gd->name,
+ hws[CLKID_GFX2DAXI + n] = clk_hw_register_gate(NULL, gd->name,
gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
gd->bit_idx, 0, &lock);
}
/* cpuclk divider is fixed to 1 */
- clks[CLKID_CPU] =
- clk_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL],
+ hws[CLKID_CPU] =
+ clk_hw_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL],
0, 1, 1);
/* twdclk is derived from cpu/3 */
- clks[CLKID_TWD] =
- clk_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
+ hws[CLKID_TWD] =
+ clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
- if (!IS_ERR(clks[n]))
+ if (!IS_ERR(hws[n]))
continue;
pr_err("%s: Unable to register leaf clock %d\n",
@@ -375,9 +382,7 @@ static void __init berlin2q_clock_setup(struct device_node *np)
}
/* register clk-provider */
- clk_data.clks = clks;
- clk_data.clk_num = MAX_CLKS;
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data);
return;
diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c
index 90897af..ea85685 100644
--- a/drivers/clk/clk-asm9260.c
+++ b/drivers/clk/clk-asm9260.c
@@ -68,8 +68,7 @@
#define HW_LCDCLKDIV 0x01fc
#define HW_ADCANACLKDIV 0x0200
-static struct clk *clks[MAX_CLKS];
-static struct clk_onecell_data clk_data;
+static struct clk_hw_onecell_data *clk_data;
static DEFINE_SPINLOCK(asm9260_clk_lock);
struct asm9260_div_clk {
@@ -267,12 +266,20 @@ static struct asm9260_mux_clock asm9260_mux_clks[] __initdata = {
static void __init asm9260_acc_init(struct device_node *np)
{
- struct clk *clk;
+ struct clk_hw *hw;
+ struct clk_hw **hws;
const char *ref_clk, *pll_clk = "pll";
u32 rate;
int n;
u32 accuracy = 0;
+ clk_data = kzalloc(sizeof(*clk_data) +
+ sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL);
+ if (!clk_data)
+ return;
+ clk_data->num = MAX_CLKS;
+ hws = clk_data->hws;
+
base = of_io_request_and_map(np, 0, np->name);
if (IS_ERR(base))
panic("%s: unable to map resource", np->name);
@@ -282,10 +289,10 @@ static void __init asm9260_acc_init(struct device_node *np)
ref_clk = of_clk_get_parent_name(np, 0);
accuracy = clk_get_accuracy(__clk_lookup(ref_clk));
- clk = clk_register_fixed_rate_with_accuracy(NULL, pll_clk,
+ hw = clk_hw_register_fixed_rate_with_accuracy(NULL, pll_clk,
ref_clk, 0, rate, accuracy);
- if (IS_ERR(clk))
+ if (IS_ERR(hw))
panic("%s: can't register REFCLK. Check DT!", np->name);
for (n = 0; n < ARRAY_SIZE(asm9260_mux_clks); n++) {
@@ -293,7 +300,7 @@ static void __init asm9260_acc_init(struct device_node *np)
mc->parent_names[0] = ref_clk;
mc->parent_names[1] = pll_clk;
- clk = clk_register_mux_table(NULL, mc->name, mc->parent_names,
+ hw = clk_hw_register_mux_table(NULL, mc->name, mc->parent_names,
mc->num_parents, mc->flags, base + mc->offset,
0, mc->mask, 0, mc->table, &asm9260_clk_lock);
}
@@ -302,7 +309,7 @@ static void __init asm9260_acc_init(struct device_node *np)
for (n = 0; n < ARRAY_SIZE(asm9260_mux_gates); n++) {
const struct asm9260_gate_data *gd = &asm9260_mux_gates[n];
- clk = clk_register_gate(NULL, gd->name,
+ hw = clk_hw_register_gate(NULL, gd->name,
gd->parent_name, gd->flags | CLK_SET_RATE_PARENT,
base + gd->reg, gd->bit_idx, 0, &asm9260_clk_lock);
}
@@ -311,7 +318,7 @@ static void __init asm9260_acc_init(struct device_node *np)
for (n = 0; n < ARRAY_SIZE(asm9260_div_clks); n++) {
const struct asm9260_div_clk *dc = &asm9260_div_clks[n];
- clks[dc->idx] = clk_register_divider(NULL, dc->name,
+ hws[dc->idx] = clk_hw_register_divider(NULL, dc->name,
dc->parent_name, CLK_SET_RATE_PARENT,
base + dc->reg, 0, 8, CLK_DIVIDER_ONE_BASED,
&asm9260_clk_lock);
@@ -321,14 +328,14 @@ static void __init asm9260_acc_init(struct device_node *np)
for (n = 0; n < ARRAY_SIZE(asm9260_ahb_gates); n++) {
const struct asm9260_gate_data *gd = &asm9260_ahb_gates[n];
- clks[gd->idx] = clk_register_gate(NULL, gd->name,
+ hws[gd->idx] = clk_hw_register_gate(NULL, gd->name,
gd->parent_name, gd->flags, base + gd->reg,
gd->bit_idx, 0, &asm9260_clk_lock);
}
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
- if (!IS_ERR(clks[n]))
+ if (!IS_ERR(hws[n]))
continue;
pr_err("%s: Unable to register leaf clock %d\n",
@@ -337,9 +344,7 @@ static void __init asm9260_acc_init(struct device_node *np)
}
/* register clk-provider */
- clk_data.clks = clks;
- clk_data.clk_num = MAX_CLKS;
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
return;
fail:
iounmap(base);
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 3294db3..5e918e7 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -392,8 +392,8 @@ static int axi_clkgen_probe(struct platform_device *pdev)
const char *parent_names[2];
const char *clk_name;
struct resource *mem;
- struct clk *clk;
unsigned int i;
+ int ret;
if (!pdev->dev.of_node)
return -ENODEV;
@@ -433,12 +433,12 @@ static int axi_clkgen_probe(struct platform_device *pdev)
axi_clkgen_mmcm_enable(axi_clkgen, false);
axi_clkgen->clk_hw.init = &init;
- clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ ret = devm_clk_hw_register(&pdev->dev, &axi_clkgen->clk_hw);
+ if (ret)
+ return ret;
- return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
- clk);
+ return of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_simple_get,
+ &axi_clkgen->clk_hw);
}
static int axi_clkgen_remove(struct platform_device *pdev)
diff --git a/drivers/clk/clk-axm5516.c b/drivers/clk/clk-axm5516.c
index c7c91a5..5d7ae33 100644
--- a/drivers/clk/clk-axm5516.c
+++ b/drivers/clk/clk-axm5516.c
@@ -516,6 +516,19 @@ static struct axxia_clk *axmclk_clocks[] = {
[AXXIA_CLK_MMC] = &clk_mmc_mux.aclk,
};
+static struct clk_hw *
+of_clk_axmclk_get(struct of_phandle_args *clkspec, void *unused)
+{
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= ARRAY_SIZE(axmclk_clocks)) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &axmclk_clocks[idx]->hw;
+}
+
static const struct regmap_config axmclk_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -530,21 +543,14 @@ static const struct of_device_id axmclk_match_table[] = {
};
MODULE_DEVICE_TABLE(of, axmclk_match_table);
-struct axmclk_priv {
- struct clk_onecell_data onecell;
- struct clk *clks[];
-};
-
static int axmclk_probe(struct platform_device *pdev)
{
void __iomem *base;
struct resource *res;
int i, ret;
struct device *dev = &pdev->dev;
- struct clk *clk;
struct regmap *regmap;
size_t num_clks;
- struct axmclk_priv *priv;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
@@ -557,29 +563,18 @@ static int axmclk_probe(struct platform_device *pdev)
num_clks = ARRAY_SIZE(axmclk_clocks);
pr_info("axmclk: supporting %zu clocks\n", num_clks);
- priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks,
- GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->onecell.clks = priv->clks;
- priv->onecell.clk_num = num_clks;
/* Update each entry with the allocated regmap and register the clock
* with the common clock framework
*/
for (i = 0; i < num_clks; i++) {
axmclk_clocks[i]->regmap = regmap;
- clk = devm_clk_register(dev, &axmclk_clocks[i]->hw);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
- priv->clks[i] = clk;
+ ret = devm_clk_hw_register(dev, &axmclk_clocks[i]->hw);
+ if (ret)
+ return ret;
}
- ret = of_clk_add_provider(dev->of_node,
- of_clk_src_onecell_get, &priv->onecell);
-
- return ret;
+ return of_clk_add_hw_provider(dev->of_node, of_clk_axmclk_get, NULL);
}
static int axmclk_remove(struct platform_device *pdev)
diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c
index 01877f6..f21d909 100644
--- a/drivers/clk/clk-cdce706.c
+++ b/drivers/clk/clk-cdce706.c
@@ -71,7 +71,6 @@ struct cdce706_hw_data {
struct cdce706_dev_data *dev_data;
unsigned idx;
unsigned parent;
- struct clk *clk;
struct clk_hw hw;
unsigned div;
unsigned mul;
@@ -81,8 +80,6 @@ struct cdce706_hw_data {
struct cdce706_dev_data {
struct i2c_client *client;
struct regmap *regmap;
- struct clk_onecell_data onecell;
- struct clk *clks[6];
struct clk *clkin_clk[2];
const char *clkin_name[2];
struct cdce706_hw_data clkin[1];
@@ -455,18 +452,19 @@ static int cdce706_register_hw(struct cdce706_dev_data *cdce,
struct clk_init_data *init)
{
unsigned i;
+ int ret;
for (i = 0; i < num_hw; ++i, ++hw) {
init->name = clk_names[i];
hw->dev_data = cdce;
hw->idx = i;
hw->hw.init = init;
- hw->clk = devm_clk_register(&cdce->client->dev,
+ ret = devm_clk_hw_register(&cdce->client->dev,
&hw->hw);
- if (IS_ERR(hw->clk)) {
+ if (ret) {
dev_err(&cdce->client->dev, "Failed to register %s\n",
clk_names[i]);
- return PTR_ERR(hw->clk);
+ return ret;
}
}
return 0;
@@ -613,13 +611,23 @@ static int cdce706_register_clkouts(struct cdce706_dev_data *cdce)
cdce->clkout[i].parent);
}
- ret = cdce706_register_hw(cdce, cdce->clkout,
- ARRAY_SIZE(cdce->clkout),
- cdce706_clkout_name, &init);
- for (i = 0; i < ARRAY_SIZE(cdce->clkout); ++i)
- cdce->clks[i] = cdce->clkout[i].clk;
+ return cdce706_register_hw(cdce, cdce->clkout,
+ ARRAY_SIZE(cdce->clkout),
+ cdce706_clkout_name, &init);
+}
- return ret;
+static struct clk_hw *
+of_clk_cdce_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct cdce706_dev_data *cdce = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= ARRAY_SIZE(cdce->clkout)) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &cdce->clkout[idx].hw;
}
static int cdce706_probe(struct i2c_client *client,
@@ -657,12 +665,8 @@ static int cdce706_probe(struct i2c_client *client,
ret = cdce706_register_clkouts(cdce);
if (ret < 0)
return ret;
- cdce->onecell.clks = cdce->clks;
- cdce->onecell.clk_num = ARRAY_SIZE(cdce->clks);
- ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
- &cdce->onecell);
-
- return ret;
+ return of_clk_add_hw_provider(client->dev.of_node, of_clk_cdce_get,
+ cdce);
}
static int cdce706_remove(struct i2c_client *client)
diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c
index 089bf88..b8459c1 100644
--- a/drivers/clk/clk-cdce925.c
+++ b/drivers/clk/clk-cdce925.c
@@ -62,8 +62,6 @@ struct clk_cdce925_chip {
struct i2c_client *i2c_client;
struct clk_cdce925_pll pll[NUMBER_OF_PLLS];
struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS];
- struct clk *dt_clk[NUMBER_OF_OUTPUTS];
- struct clk_onecell_data onecell;
};
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
@@ -557,6 +555,20 @@ static int cdce925_regmap_i2c_read(void *context,
return -EIO;
}
+static struct clk_hw *
+of_clk_cdce925_get(struct of_phandle_args *clkspec, void *_data)
+{
+ struct clk_cdce925_chip *data = _data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= ARRAY_SIZE(data->clk)) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &data->clk[idx].hw;
+}
+
/* The CDCE925 uses a funky way to read/write registers. Bulk mode is
* just weird, so just use the single byte mode exclusively. */
static struct regmap_bus regmap_cdce925_bus = {
@@ -572,7 +584,6 @@ static int cdce925_probe(struct i2c_client *client,
const char *parent_name;
const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,};
struct clk_init_data init;
- struct clk *clk;
u32 value;
int i;
int err;
@@ -622,10 +633,9 @@ static int cdce925_probe(struct i2c_client *client,
data->pll[i].chip = data;
data->pll[i].hw.init = &init;
data->pll[i].index = i;
- clk = devm_clk_register(&client->dev, &data->pll[i].hw);
- if (IS_ERR(clk)) {
+ err = devm_clk_hw_register(&client->dev, &data->pll[i].hw);
+ if (err) {
dev_err(&client->dev, "Failed register PLL %d\n", i);
- err = PTR_ERR(clk);
goto error;
}
sprintf(child_name, "PLL%d", i+1);
@@ -634,7 +644,7 @@ static int cdce925_probe(struct i2c_client *client,
continue;
if (!of_property_read_u32(np_output,
"clock-frequency", &value)) {
- err = clk_set_rate(clk, value);
+ err = clk_set_rate(data->pll[i].hw.clk, value);
if (err)
dev_err(&client->dev,
"unable to set PLL frequency %ud\n",
@@ -663,14 +673,12 @@ static int cdce925_probe(struct i2c_client *client,
data->clk[0].hw.init = &init;
data->clk[0].index = 0;
data->clk[0].pdiv = 1;
- clk = devm_clk_register(&client->dev, &data->clk[0].hw);
+ err = devm_clk_hw_register(&client->dev, &data->clk[0].hw);
kfree(init.name); /* clock framework made a copy of the name */
- if (IS_ERR(clk)) {
+ if (err) {
dev_err(&client->dev, "clock registration Y1 failed\n");
- err = PTR_ERR(clk);
goto error;
}
- data->dt_clk[0] = clk;
/* Register output clocks Y2 .. Y5*/
init.ops = &cdce925_clk_ops;
@@ -695,21 +703,17 @@ static int cdce925_probe(struct i2c_client *client,
init.parent_names = &pll_clk_name[1];
break;
}
- clk = devm_clk_register(&client->dev, &data->clk[i].hw);
+ err = devm_clk_hw_register(&client->dev, &data->clk[i].hw);
kfree(init.name); /* clock framework made a copy of the name */
- if (IS_ERR(clk)) {
+ if (err) {
dev_err(&client->dev, "clock registration failed\n");
- err = PTR_ERR(clk);
goto error;
}
- data->dt_clk[i] = clk;
}
/* Register the output clocks */
- data->onecell.clk_num = NUMBER_OF_OUTPUTS;
- data->onecell.clks = data->dt_clk;
- err = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
- &data->onecell);
+ err = of_clk_add_hw_provider(client->dev.of_node, of_clk_cdce925_get,
+ data);
if (err)
dev_err(&client->dev, "unable to add OF clock provider\n");
diff --git a/drivers/clk/clk-clps711x.c b/drivers/clk/clk-clps711x.c
index adaf109..9193f64 100644
--- a/drivers/clk/clk-clps711x.c
+++ b/drivers/clk/clk-clps711x.c
@@ -40,9 +40,8 @@ static const struct clk_div_table timer_div_table[] = {
};
struct clps711x_clk {
- struct clk_onecell_data clk_data;
- spinlock_t lock;
- struct clk *clks[CLPS711X_CLK_MAX];
+ spinlock_t lock;
+ struct clk_hw_onecell_data clk_data;
};
static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base,
@@ -55,7 +54,9 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base,
if (!base)
return ERR_PTR(-ENOMEM);
- clps711x_clk = kzalloc(sizeof(*clps711x_clk), GFP_KERNEL);
+ clps711x_clk = kzalloc(sizeof(*clps711x_clk) +
+ sizeof(*clps711x_clk->clk_data.hws) * CLPS711X_CLK_MAX,
+ GFP_KERNEL);
if (!clps711x_clk)
return ERR_PTR(-ENOMEM);
@@ -106,40 +107,40 @@ static struct clps711x_clk * __init _clps711x_clk_init(void __iomem *base,
tmp |= SYSCON1_TC2M | SYSCON1_TC2S;
writel(tmp, base + CLPS711X_SYSCON1);
- clps711x_clk->clks[CLPS711X_CLK_DUMMY] =
- clk_register_fixed_rate(NULL, "dummy", NULL, 0, 0);
- clps711x_clk->clks[CLPS711X_CLK_CPU] =
- clk_register_fixed_rate(NULL, "cpu", NULL, 0, f_cpu);
- clps711x_clk->clks[CLPS711X_CLK_BUS] =
- clk_register_fixed_rate(NULL, "bus", NULL, 0, f_bus);
- clps711x_clk->clks[CLPS711X_CLK_PLL] =
- clk_register_fixed_rate(NULL, "pll", NULL, 0, f_pll);
- clps711x_clk->clks[CLPS711X_CLK_TIMERREF] =
- clk_register_fixed_rate(NULL, "timer_ref", NULL, 0, f_tim);
- clps711x_clk->clks[CLPS711X_CLK_TIMER1] =
- clk_register_divider_table(NULL, "timer1", "timer_ref", 0,
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_DUMMY] =
+ clk_hw_register_fixed_rate(NULL, "dummy", NULL, 0, 0);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_CPU] =
+ clk_hw_register_fixed_rate(NULL, "cpu", NULL, 0, f_cpu);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_BUS] =
+ clk_hw_register_fixed_rate(NULL, "bus", NULL, 0, f_bus);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_PLL] =
+ clk_hw_register_fixed_rate(NULL, "pll", NULL, 0, f_pll);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMERREF] =
+ clk_hw_register_fixed_rate(NULL, "timer_ref", NULL, 0, f_tim);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1] =
+ clk_hw_register_divider_table(NULL, "timer1", "timer_ref", 0,
base + CLPS711X_SYSCON1, 5, 1, 0,
timer_div_table, &clps711x_clk->lock);
- clps711x_clk->clks[CLPS711X_CLK_TIMER2] =
- clk_register_divider_table(NULL, "timer2", "timer_ref", 0,
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2] =
+ clk_hw_register_divider_table(NULL, "timer2", "timer_ref", 0,
base + CLPS711X_SYSCON1, 7, 1, 0,
timer_div_table, &clps711x_clk->lock);
- clps711x_clk->clks[CLPS711X_CLK_PWM] =
- clk_register_fixed_rate(NULL, "pwm", NULL, 0, f_pwm);
- clps711x_clk->clks[CLPS711X_CLK_SPIREF] =
- clk_register_fixed_rate(NULL, "spi_ref", NULL, 0, f_spi);
- clps711x_clk->clks[CLPS711X_CLK_SPI] =
- clk_register_divider_table(NULL, "spi", "spi_ref", 0,
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM] =
+ clk_hw_register_fixed_rate(NULL, "pwm", NULL, 0, f_pwm);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_SPIREF] =
+ clk_hw_register_fixed_rate(NULL, "spi_ref", NULL, 0, f_spi);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_SPI] =
+ clk_hw_register_divider_table(NULL, "spi", "spi_ref", 0,
base + CLPS711X_SYSCON1, 16, 2, 0,
spi_div_table, &clps711x_clk->lock);
- clps711x_clk->clks[CLPS711X_CLK_UART] =
- clk_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10);
- clps711x_clk->clks[CLPS711X_CLK_TICK] =
- clk_register_fixed_rate(NULL, "tick", NULL, 0, 64);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_UART] =
+ clk_hw_register_fixed_factor(NULL, "uart", "bus", 0, 1, 10);
+ clps711x_clk->clk_data.hws[CLPS711X_CLK_TICK] =
+ clk_hw_register_fixed_rate(NULL, "tick", NULL, 0, 64);
for (i = 0; i < CLPS711X_CLK_MAX; i++)
- if (IS_ERR(clps711x_clk->clks[i]))
+ if (IS_ERR(clps711x_clk->clk_data.hws[i]))
pr_err("clk %i: register failed with %ld\n",
- i, PTR_ERR(clps711x_clk->clks[i]));
+ i, PTR_ERR(clps711x_clk->clk_data.hws[i]));
return clps711x_clk;
}
@@ -153,17 +154,17 @@ void __init clps711x_clk_init(void __iomem *base)
BUG_ON(IS_ERR(clps711x_clk));
/* Clocksource */
- clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_TIMER1],
+ clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER1],
NULL, "clps711x-timer.0");
- clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_TIMER2],
+ clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_TIMER2],
NULL, "clps711x-timer.1");
/* Drivers */
- clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_PWM],
+ clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_PWM],
NULL, "clps711x-pwm");
- clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_UART],
+ clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART],
NULL, "clps711x-uart.0");
- clk_register_clkdev(clps711x_clk->clks[CLPS711X_CLK_UART],
+ clk_hw_register_clkdev(clps711x_clk->clk_data.hws[CLPS711X_CLK_UART],
NULL, "clps711x-uart.1");
}
@@ -179,10 +180,9 @@ static void __init clps711x_clk_init_dt(struct device_node *np)
clps711x_clk = _clps711x_clk_init(base, fref);
BUG_ON(IS_ERR(clps711x_clk));
- clps711x_clk->clk_data.clks = clps711x_clk->clks;
- clps711x_clk->clk_data.clk_num = CLPS711X_CLK_MAX;
- of_clk_add_provider(np, of_clk_src_onecell_get,
- &clps711x_clk->clk_data);
+ clps711x_clk->clk_data.num = CLPS711X_CLK_MAX;
+ of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
+ &clps711x_clk->clk_data);
}
CLK_OF_DECLARE(clps711x, "cirrus,ep7209-clk", clps711x_clk_init_dt);
#endif
diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c
index 7379de8..021f3da 100644
--- a/drivers/clk/clk-cs2000-cp.c
+++ b/drivers/clk/clk-cs2000-cp.c
@@ -59,7 +59,6 @@ struct cs2000_priv {
struct i2c_client *client;
struct clk *clk_in;
struct clk *ref_clk;
- struct clk *clk_out;
};
static const struct of_device_id cs2000_of_match[] = {
@@ -371,7 +370,6 @@ static int cs2000_clk_register(struct cs2000_priv *priv)
struct device_node *np = dev->of_node;
struct clk_init_data init;
const char *name = np->name;
- struct clk *clk;
static const char *parent_names[CLK_MAX];
int ch = 0; /* it uses ch0 only at this point */
int rate;
@@ -400,18 +398,16 @@ static int cs2000_clk_register(struct cs2000_priv *priv)
priv->hw.init = &init;
- clk = clk_register(dev, &priv->hw);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ ret = clk_hw_register(dev, &priv->hw);
+ if (ret)
+ return ret;
- ret = of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &priv->hw);
if (ret < 0) {
- clk_unregister(clk);
+ clk_hw_unregister(&priv->hw);
return ret;
}
- priv->clk_out = clk;
-
return 0;
}
@@ -454,7 +450,7 @@ static int cs2000_remove(struct i2c_client *client)
of_clk_del_provider(np);
- clk_unregister(priv->clk_out);
+ clk_hw_unregister(&priv->hw);
return 0;
}
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index a0f55bc..96386ff 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -352,7 +352,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
/* if read only, just return current value */
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
- bestdiv = readl(divider->reg) >> divider->shift;
+ bestdiv = clk_readl(divider->reg) >> divider->shift;
bestdiv &= div_mask(divider->width);
bestdiv = _get_div(divider->table, bestdiv, divider->flags,
divider->width);
diff --git a/drivers/clk/clk-efm32gg.c b/drivers/clk/clk-efm32gg.c
index 22e4c65..8802a2d 100644
--- a/drivers/clk/clk-efm32gg.c
+++ b/drivers/clk/clk-efm32gg.c
@@ -10,24 +10,31 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/slab.h>
#include <dt-bindings/clock/efm32-cmu.h>
#define CMU_HFPERCLKEN0 0x44
+#define CMU_MAX_CLKS 37
-static struct clk *clk[37];
-static struct clk_onecell_data clk_data = {
- .clks = clk,
- .clk_num = ARRAY_SIZE(clk),
-};
+static struct clk_hw_onecell_data *clk_data;
static void __init efm32gg_cmu_init(struct device_node *np)
{
int i;
void __iomem *base;
+ struct clk_hw **hws;
- for (i = 0; i < ARRAY_SIZE(clk); ++i)
- clk[i] = ERR_PTR(-ENOENT);
+ clk_data = kzalloc(sizeof(*clk_data) +
+ sizeof(*clk_data->hws) * CMU_MAX_CLKS, GFP_KERNEL);
+
+ if (!clk_data)
+ return;
+
+ hws = clk_data->hws;
+
+ for (i = 0; i < CMU_MAX_CLKS; ++i)
+ hws[i] = ERR_PTR(-ENOENT);
base = of_iomap(np, 0);
if (!base) {
@@ -35,46 +42,46 @@ static void __init efm32gg_cmu_init(struct device_node *np)
return;
}
- clk[clk_HFXO] = clk_register_fixed_rate(NULL, "HFXO", NULL,
- 0, 48000000);
+ hws[clk_HFXO] = clk_hw_register_fixed_rate(NULL, "HFXO", NULL, 0,
+ 48000000);
- clk[clk_HFPERCLKUSART0] = clk_register_gate(NULL, "HFPERCLK.USART0",
+ hws[clk_HFPERCLKUSART0] = clk_hw_register_gate(NULL, "HFPERCLK.USART0",
"HFXO", 0, base + CMU_HFPERCLKEN0, 0, 0, NULL);
- clk[clk_HFPERCLKUSART1] = clk_register_gate(NULL, "HFPERCLK.USART1",
+ hws[clk_HFPERCLKUSART1] = clk_hw_register_gate(NULL, "HFPERCLK.USART1",
"HFXO", 0, base + CMU_HFPERCLKEN0, 1, 0, NULL);
- clk[clk_HFPERCLKUSART2] = clk_register_gate(NULL, "HFPERCLK.USART2",
+ hws[clk_HFPERCLKUSART2] = clk_hw_register_gate(NULL, "HFPERCLK.USART2",
"HFXO", 0, base + CMU_HFPERCLKEN0, 2, 0, NULL);
- clk[clk_HFPERCLKUART0] = clk_register_gate(NULL, "HFPERCLK.UART0",
+ hws[clk_HFPERCLKUART0] = clk_hw_register_gate(NULL, "HFPERCLK.UART0",
"HFXO", 0, base + CMU_HFPERCLKEN0, 3, 0, NULL);
- clk[clk_HFPERCLKUART1] = clk_register_gate(NULL, "HFPERCLK.UART1",
+ hws[clk_HFPERCLKUART1] = clk_hw_register_gate(NULL, "HFPERCLK.UART1",
"HFXO", 0, base + CMU_HFPERCLKEN0, 4, 0, NULL);
- clk[clk_HFPERCLKTIMER0] = clk_register_gate(NULL, "HFPERCLK.TIMER0",
+ hws[clk_HFPERCLKTIMER0] = clk_hw_register_gate(NULL, "HFPERCLK.TIMER0",
"HFXO", 0, base + CMU_HFPERCLKEN0, 5, 0, NULL);
- clk[clk_HFPERCLKTIMER1] = clk_register_gate(NULL, "HFPERCLK.TIMER1",
+ hws[clk_HFPERCLKTIMER1] = clk_hw_register_gate(NULL, "HFPERCLK.TIMER1",
"HFXO", 0, base + CMU_HFPERCLKEN0, 6, 0, NULL);
- clk[clk_HFPERCLKTIMER2] = clk_register_gate(NULL, "HFPERCLK.TIMER2",
+ hws[clk_HFPERCLKTIMER2] = clk_hw_register_gate(NULL, "HFPERCLK.TIMER2",
"HFXO", 0, base + CMU_HFPERCLKEN0, 7, 0, NULL);
- clk[clk_HFPERCLKTIMER3] = clk_register_gate(NULL, "HFPERCLK.TIMER3",
+ hws[clk_HFPERCLKTIMER3] = clk_hw_register_gate(NULL, "HFPERCLK.TIMER3",
"HFXO", 0, base + CMU_HFPERCLKEN0, 8, 0, NULL);
- clk[clk_HFPERCLKACMP0] = clk_register_gate(NULL, "HFPERCLK.ACMP0",
+ hws[clk_HFPERCLKACMP0] = clk_hw_register_gate(NULL, "HFPERCLK.ACMP0",
"HFXO", 0, base + CMU_HFPERCLKEN0, 9, 0, NULL);
- clk[clk_HFPERCLKACMP1] = clk_register_gate(NULL, "HFPERCLK.ACMP1",
+ hws[clk_HFPERCLKACMP1] = clk_hw_register_gate(NULL, "HFPERCLK.ACMP1",
"HFXO", 0, base + CMU_HFPERCLKEN0, 10, 0, NULL);
- clk[clk_HFPERCLKI2C0] = clk_register_gate(NULL, "HFPERCLK.I2C0",
+ hws[clk_HFPERCLKI2C0] = clk_hw_register_gate(NULL, "HFPERCLK.I2C0",
"HFXO", 0, base + CMU_HFPERCLKEN0, 11, 0, NULL);
- clk[clk_HFPERCLKI2C1] = clk_register_gate(NULL, "HFPERCLK.I2C1",
+ hws[clk_HFPERCLKI2C1] = clk_hw_register_gate(NULL, "HFPERCLK.I2C1",
"HFXO", 0, base + CMU_HFPERCLKEN0, 12, 0, NULL);
- clk[clk_HFPERCLKGPIO] = clk_register_gate(NULL, "HFPERCLK.GPIO",
+ hws[clk_HFPERCLKGPIO] = clk_hw_register_gate(NULL, "HFPERCLK.GPIO",
"HFXO", 0, base + CMU_HFPERCLKEN0, 13, 0, NULL);
- clk[clk_HFPERCLKVCMP] = clk_register_gate(NULL, "HFPERCLK.VCMP",
+ hws[clk_HFPERCLKVCMP] = clk_hw_register_gate(NULL, "HFPERCLK.VCMP",
"HFXO", 0, base + CMU_HFPERCLKEN0, 14, 0, NULL);
- clk[clk_HFPERCLKPRS] = clk_register_gate(NULL, "HFPERCLK.PRS",
+ hws[clk_HFPERCLKPRS] = clk_hw_register_gate(NULL, "HFPERCLK.PRS",
"HFXO", 0, base + CMU_HFPERCLKEN0, 15, 0, NULL);
- clk[clk_HFPERCLKADC0] = clk_register_gate(NULL, "HFPERCLK.ADC0",
+ hws[clk_HFPERCLKADC0] = clk_hw_register_gate(NULL, "HFPERCLK.ADC0",
"HFXO", 0, base + CMU_HFPERCLKEN0, 16, 0, NULL);
- clk[clk_HFPERCLKDAC0] = clk_register_gate(NULL, "HFPERCLK.DAC0",
+ hws[clk_HFPERCLKDAC0] = clk_hw_register_gate(NULL, "HFPERCLK.DAC0",
"HFXO", 0, base + CMU_HFPERCLKEN0, 17, 0, NULL);
- of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data);
}
CLK_OF_DECLARE(efm32ggcmu, "efm32gg,cmu", efm32gg_cmu_init);
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index 4db3be2..a5d402d 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
/*
* DOC: basic fixed multiplier and divider clock that cannot gate
@@ -147,27 +148,25 @@ static const struct of_device_id set_rate_parent_matches[] = {
{ /* Sentinel */ },
};
-/**
- * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
- */
-void __init of_fixed_factor_clk_setup(struct device_node *node)
+static struct clk *_of_fixed_factor_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parent_name;
unsigned long flags = 0;
u32 div, mult;
+ int ret;
if (of_property_read_u32(node, "clock-div", &div)) {
pr_err("%s Fixed factor clock <%s> must have a clock-div property\n",
__func__, node->name);
- return;
+ return ERR_PTR(-EIO);
}
if (of_property_read_u32(node, "clock-mult", &mult)) {
pr_err("%s Fixed factor clock <%s> must have a clock-mult property\n",
__func__, node->name);
- return;
+ return ERR_PTR(-EIO);
}
of_property_read_string(node, "clock-output-names", &clk_name);
@@ -178,10 +177,67 @@ void __init of_fixed_factor_clk_setup(struct device_node *node)
clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags,
mult, div);
- if (!IS_ERR(clk))
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (ret) {
+ clk_unregister(clk);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+/**
+ * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
+ */
+void __init of_fixed_factor_clk_setup(struct device_node *node)
+{
+ _of_fixed_factor_clk_setup(node);
}
-EXPORT_SYMBOL_GPL(of_fixed_factor_clk_setup);
CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
of_fixed_factor_clk_setup);
+
+static int of_fixed_factor_clk_remove(struct platform_device *pdev)
+{
+ struct clk *clk = platform_get_drvdata(pdev);
+
+ clk_unregister_fixed_factor(clk);
+
+ return 0;
+}
+
+static int of_fixed_factor_clk_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+
+ /*
+ * This function is not executed when of_fixed_factor_clk_setup
+ * succeeded.
+ */
+ clk = _of_fixed_factor_clk_setup(pdev->dev.of_node);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ platform_set_drvdata(pdev, clk);
+
+ return 0;
+}
+
+static const struct of_device_id of_fixed_factor_clk_ids[] = {
+ { .compatible = "fixed-factor-clock" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, of_fixed_factor_clk_ids);
+
+static struct platform_driver of_fixed_factor_clk_driver = {
+ .driver = {
+ .name = "of_fixed_factor_clk",
+ .of_match_table = of_fixed_factor_clk_ids,
+ },
+ .probe = of_fixed_factor_clk_probe,
+ .remove = of_fixed_factor_clk_remove,
+};
+builtin_platform_driver(of_fixed_factor_clk_driver);
#endif
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
index 2edb393..b5c46b3 100644
--- a/drivers/clk/clk-fixed-rate.c
+++ b/drivers/clk/clk-fixed-rate.c
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/platform_device.h>
/*
* DOC: basic fixed-rate clock that cannot gate
@@ -157,18 +158,16 @@ void clk_hw_unregister_fixed_rate(struct clk_hw *hw)
EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_rate);
#ifdef CONFIG_OF
-/**
- * of_fixed_clk_setup() - Setup function for simple fixed rate clock
- */
-void of_fixed_clk_setup(struct device_node *node)
+static struct clk *_of_fixed_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
u32 rate;
u32 accuracy = 0;
+ int ret;
if (of_property_read_u32(node, "clock-frequency", &rate))
- return;
+ return ERR_PTR(-EIO);
of_property_read_u32(node, "clock-accuracy", &accuracy);
@@ -176,9 +175,66 @@ void of_fixed_clk_setup(struct device_node *node)
clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,
0, rate, accuracy);
- if (!IS_ERR(clk))
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (IS_ERR(clk))
+ return clk;
+
+ ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (ret) {
+ clk_unregister(clk);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+/**
+ * of_fixed_clk_setup() - Setup function for simple fixed rate clock
+ */
+void __init of_fixed_clk_setup(struct device_node *node)
+{
+ _of_fixed_clk_setup(node);
}
-EXPORT_SYMBOL_GPL(of_fixed_clk_setup);
CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
+
+static int of_fixed_clk_remove(struct platform_device *pdev)
+{
+ struct clk *clk = platform_get_drvdata(pdev);
+
+ clk_unregister_fixed_rate(clk);
+
+ return 0;
+}
+
+static int of_fixed_clk_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+
+ /*
+ * This function is not executed when of_fixed_clk_setup
+ * succeeded.
+ */
+ clk = _of_fixed_clk_setup(pdev->dev.of_node);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ platform_set_drvdata(pdev, clk);
+
+ return 0;
+}
+
+static const struct of_device_id of_fixed_clk_ids[] = {
+ { .compatible = "fixed-clock" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, of_fixed_clk_ids);
+
+static struct platform_driver of_fixed_clk_driver = {
+ .driver = {
+ .name = "of_fixed_clk",
+ .of_match_table = of_fixed_clk_ids,
+ },
+ .probe = of_fixed_clk_probe,
+ .remove = of_fixed_clk_remove,
+};
+builtin_platform_driver(of_fixed_clk_driver);
#endif
diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c
deleted file mode 100644
index 5097831..0000000
--- a/drivers/clk/clk-ls1x.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-
-#include <loongson1.h>
-
-#define OSC (33 * 1000000)
-#define DIV_APB 2
-
-static DEFINE_SPINLOCK(_lock);
-
-static int ls1x_pll_clk_enable(struct clk_hw *hw)
-{
- return 0;
-}
-
-static void ls1x_pll_clk_disable(struct clk_hw *hw)
-{
-}
-
-static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- u32 pll, rate;
-
- pll = __raw_readl(LS1X_CLK_PLL_FREQ);
- rate = 12 + (pll & 0x3f) + (((pll >> 8) & 0x3ff) >> 10);
- rate *= OSC;
- rate >>= 1;
-
- return rate;
-}
-
-static const struct clk_ops ls1x_pll_clk_ops = {
- .enable = ls1x_pll_clk_enable,
- .disable = ls1x_pll_clk_disable,
- .recalc_rate = ls1x_pll_recalc_rate,
-};
-
-static struct clk *__init clk_register_pll(struct device *dev,
- const char *name,
- const char *parent_name,
- unsigned long flags)
-{
- struct clk_hw *hw;
- struct clk *clk;
- struct clk_init_data init;
-
- /* allocate the divider */
- hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL);
- if (!hw) {
- pr_err("%s: could not allocate clk_hw\n", __func__);
- return ERR_PTR(-ENOMEM);
- }
-
- init.name = name;
- init.ops = &ls1x_pll_clk_ops;
- init.flags = flags | CLK_IS_BASIC;
- init.parent_names = (parent_name ? &parent_name : NULL);
- init.num_parents = (parent_name ? 1 : 0);
- hw->init = &init;
-
- /* register the clock */
- clk = clk_register(dev, hw);
-
- if (IS_ERR(clk))
- kfree(hw);
-
- return clk;
-}
-
-static const char * const cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", };
-static const char * const ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", };
-static const char * const dc_parents[] = { "dc_clk_div", "osc_33m_clk", };
-
-void __init ls1x_clk_init(void)
-{
- struct clk *clk;
-
- clk = clk_register_fixed_rate(NULL, "osc_33m_clk", NULL, 0, OSC);
- clk_register_clkdev(clk, "osc_33m_clk", NULL);
-
- /* clock derived from 33 MHz OSC clk */
- clk = clk_register_pll(NULL, "pll_clk", "osc_33m_clk", 0);
- clk_register_clkdev(clk, "pll_clk", NULL);
-
- /* clock derived from PLL clk */
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ CPU CLK
- * \___ PLL ___ CPU DIV ___| |
- * |_____|
- */
- clk = clk_register_divider(NULL, "cpu_clk_div", "pll_clk",
- CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV,
- DIV_CPU_SHIFT, DIV_CPU_WIDTH,
- CLK_DIVIDER_ONE_BASED |
- CLK_DIVIDER_ROUND_CLOSEST, &_lock);
- clk_register_clkdev(clk, "cpu_clk_div", NULL);
- clk = clk_register_mux(NULL, "cpu_clk", cpu_parents,
- ARRAY_SIZE(cpu_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock);
- clk_register_clkdev(clk, "cpu_clk", NULL);
-
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ DC CLK
- * \___ PLL ___ DC DIV ___| |
- * |_____|
- */
- clk = clk_register_divider(NULL, "dc_clk_div", "pll_clk",
- 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
- DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
- clk_register_clkdev(clk, "dc_clk_div", NULL);
- clk = clk_register_mux(NULL, "dc_clk", dc_parents,
- ARRAY_SIZE(dc_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock);
- clk_register_clkdev(clk, "dc_clk", NULL);
-
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ DDR CLK
- * \___ PLL ___ DDR DIV ___| |
- * |_____|
- */
- clk = clk_register_divider(NULL, "ahb_clk_div", "pll_clk",
- 0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
- DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED,
- &_lock);
- clk_register_clkdev(clk, "ahb_clk_div", NULL);
- clk = clk_register_mux(NULL, "ahb_clk", ahb_parents,
- ARRAY_SIZE(ahb_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock);
- clk_register_clkdev(clk, "ahb_clk", NULL);
- clk_register_clkdev(clk, "stmmaceth", NULL);
-
- /* clock derived from AHB clk */
- /* APB clk is always half of the AHB clk */
- clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
- DIV_APB);
- clk_register_clkdev(clk, "apb_clk", NULL);
- clk_register_clkdev(clk, "ls1x_i2c", NULL);
- clk_register_clkdev(clk, "ls1x_pwmtimer", NULL);
- clk_register_clkdev(clk, "ls1x_spi", NULL);
- clk_register_clkdev(clk, "ls1x_wdt", NULL);
- clk_register_clkdev(clk, "serial8250", NULL);
-}
diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c
deleted file mode 100644
index 35af9cb..0000000
--- a/drivers/clk/clk-max-gen.c
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * clk-max-gen.c - Generic clock driver for Maxim PMICs clocks
- *
- * Copyright (C) 2014 Google, Inc
- *
- * Copyright (C) 2012 Samsung Electornics
- * Jonghwa Lee <jonghwa3.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver is based on clk-max77686.c
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/regmap.h>
-#include <linux/platform_device.h>
-#include <linux/clk-provider.h>
-#include <linux/mutex.h>
-#include <linux/clkdev.h>
-#include <linux/of.h>
-#include <linux/export.h>
-
-#include "clk-max-gen.h"
-
-struct max_gen_clk {
- struct regmap *regmap;
- u32 mask;
- u32 reg;
- struct clk_hw hw;
-};
-
-static struct max_gen_clk *to_max_gen_clk(struct clk_hw *hw)
-{
- return container_of(hw, struct max_gen_clk, hw);
-}
-
-static int max_gen_clk_prepare(struct clk_hw *hw)
-{
- struct max_gen_clk *max_gen = to_max_gen_clk(hw);
-
- return regmap_update_bits(max_gen->regmap, max_gen->reg,
- max_gen->mask, max_gen->mask);
-}
-
-static void max_gen_clk_unprepare(struct clk_hw *hw)
-{
- struct max_gen_clk *max_gen = to_max_gen_clk(hw);
-
- regmap_update_bits(max_gen->regmap, max_gen->reg,
- max_gen->mask, ~max_gen->mask);
-}
-
-static int max_gen_clk_is_prepared(struct clk_hw *hw)
-{
- struct max_gen_clk *max_gen = to_max_gen_clk(hw);
- int ret;
- u32 val;
-
- ret = regmap_read(max_gen->regmap, max_gen->reg, &val);
-
- if (ret < 0)
- return -EINVAL;
-
- return val & max_gen->mask;
-}
-
-static unsigned long max_gen_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- return 32768;
-}
-
-struct clk_ops max_gen_clk_ops = {
- .prepare = max_gen_clk_prepare,
- .unprepare = max_gen_clk_unprepare,
- .is_prepared = max_gen_clk_is_prepared,
- .recalc_rate = max_gen_recalc_rate,
-};
-EXPORT_SYMBOL_GPL(max_gen_clk_ops);
-
-static struct clk *max_gen_clk_register(struct device *dev,
- struct max_gen_clk *max_gen)
-{
- struct clk *clk;
- struct clk_hw *hw = &max_gen->hw;
- int ret;
-
- clk = devm_clk_register(dev, hw);
- if (IS_ERR(clk))
- return clk;
-
- ret = clk_register_clkdev(clk, hw->init->name, NULL);
-
- if (ret)
- return ERR_PTR(ret);
-
- return clk;
-}
-
-int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap,
- u32 reg, struct clk_init_data *clks_init, int num_init)
-{
- int i, ret;
- struct max_gen_clk *max_gen_clks;
- struct clk **clocks;
- struct device *dev = pdev->dev.parent;
- const char *clk_name;
- struct clk_init_data *init;
-
- clocks = devm_kzalloc(dev, sizeof(struct clk *) * num_init, GFP_KERNEL);
- if (!clocks)
- return -ENOMEM;
-
- max_gen_clks = devm_kzalloc(dev, sizeof(struct max_gen_clk)
- * num_init, GFP_KERNEL);
- if (!max_gen_clks)
- return -ENOMEM;
-
- for (i = 0; i < num_init; i++) {
- max_gen_clks[i].regmap = regmap;
- max_gen_clks[i].mask = 1 << i;
- max_gen_clks[i].reg = reg;
-
- init = devm_kzalloc(dev, sizeof(*init), GFP_KERNEL);
- if (!init)
- return -ENOMEM;
-
- if (dev->of_node &&
- !of_property_read_string_index(dev->of_node,
- "clock-output-names",
- i, &clk_name))
- init->name = clk_name;
- else
- init->name = clks_init[i].name;
-
- init->ops = clks_init[i].ops;
- init->flags = clks_init[i].flags;
-
- max_gen_clks[i].hw.init = init;
-
- clocks[i] = max_gen_clk_register(dev, &max_gen_clks[i]);
- if (IS_ERR(clocks[i])) {
- ret = PTR_ERR(clocks[i]);
- dev_err(dev, "failed to register %s\n",
- max_gen_clks[i].hw.init->name);
- return ret;
- }
- }
-
- platform_set_drvdata(pdev, clocks);
-
- if (dev->of_node) {
- struct clk_onecell_data *of_data;
-
- of_data = devm_kzalloc(dev, sizeof(*of_data), GFP_KERNEL);
- if (!of_data)
- return -ENOMEM;
-
- of_data->clks = clocks;
- of_data->clk_num = num_init;
- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
- of_data);
-
- if (ret) {
- dev_err(dev, "failed to register OF clock provider\n");
- return ret;
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(max_gen_clk_probe);
-
-int max_gen_clk_remove(struct platform_device *pdev, int num_init)
-{
- struct device *dev = pdev->dev.parent;
-
- if (dev->of_node)
- of_clk_del_provider(dev->of_node);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(max_gen_clk_remove);
diff --git a/drivers/clk/clk-max-gen.h b/drivers/clk/clk-max-gen.h
deleted file mode 100644
index 997e86f..0000000
--- a/drivers/clk/clk-max-gen.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * clk-max-gen.h - Generic clock driver for Maxim PMICs clocks
- *
- * Copyright (C) 2014 Google, Inc
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __CLK_MAX_GEN_H__
-#define __CLK_MAX_GEN_H__
-
-#include <linux/types.h>
-#include <linux/device.h>
-#include <linux/clkdev.h>
-#include <linux/regmap.h>
-#include <linux/platform_device.h>
-
-int max_gen_clk_probe(struct platform_device *pdev, struct regmap *regmap,
- u32 reg, struct clk_init_data *clks_init, int num_init);
-int max_gen_clk_remove(struct platform_device *pdev, int num_init);
-extern struct clk_ops max_gen_clk_ops;
-
-#endif /* __CLK_MAX_GEN_H__ */
diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c
index 9b6f277..b637f59 100644
--- a/drivers/clk/clk-max77686.c
+++ b/drivers/clk/clk-max77686.c
@@ -1,5 +1,5 @@
/*
- * clk-max77686.c - Clock driver for Maxim 77686
+ * clk-max77686.c - Clock driver for Maxim 77686/MAX77802
*
* Copyright (C) 2012 Samsung Electornics
* Jonghwa Lee <jonghwa3.lee@samsung.com>
@@ -25,46 +25,284 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/mfd/max77620.h>
#include <linux/mfd/max77686.h>
#include <linux/mfd/max77686-private.h>
#include <linux/clk-provider.h>
#include <linux/mutex.h>
#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
#include <dt-bindings/clock/maxim,max77686.h>
-#include "clk-max-gen.h"
+#include <dt-bindings/clock/maxim,max77802.h>
+#include <dt-bindings/clock/maxim,max77620.h>
-static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = {
+#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
+
+enum max77686_chip_name {
+ CHIP_MAX77686,
+ CHIP_MAX77802,
+ CHIP_MAX77620,
+};
+
+struct max77686_hw_clk_info {
+ const char *name;
+ u32 clk_reg;
+ u32 clk_enable_mask;
+ u32 flags;
+};
+
+struct max77686_clk_init_data {
+ struct regmap *regmap;
+ struct clk_hw hw;
+ struct clk_init_data clk_idata;
+ const struct max77686_hw_clk_info *clk_info;
+};
+
+struct max77686_clk_driver_data {
+ enum max77686_chip_name chip;
+ struct max77686_clk_init_data *max_clk_data;
+ size_t num_clks;
+};
+
+static const struct
+max77686_hw_clk_info max77686_hw_clks_info[MAX77686_CLKS_NUM] = {
[MAX77686_CLK_AP] = {
.name = "32khz_ap",
- .ops = &max_gen_clk_ops,
+ .clk_reg = MAX77686_REG_32KHZ,
+ .clk_enable_mask = BIT(MAX77686_CLK_AP),
},
[MAX77686_CLK_CP] = {
.name = "32khz_cp",
- .ops = &max_gen_clk_ops,
+ .clk_reg = MAX77686_REG_32KHZ,
+ .clk_enable_mask = BIT(MAX77686_CLK_CP),
},
[MAX77686_CLK_PMIC] = {
.name = "32khz_pmic",
- .ops = &max_gen_clk_ops,
+ .clk_reg = MAX77686_REG_32KHZ,
+ .clk_enable_mask = BIT(MAX77686_CLK_PMIC),
+ },
+};
+
+static const struct
+max77686_hw_clk_info max77802_hw_clks_info[MAX77802_CLKS_NUM] = {
+ [MAX77802_CLK_32K_AP] = {
+ .name = "32khz_ap",
+ .clk_reg = MAX77802_REG_32KHZ,
+ .clk_enable_mask = BIT(MAX77802_CLK_32K_AP),
+ },
+ [MAX77802_CLK_32K_CP] = {
+ .name = "32khz_cp",
+ .clk_reg = MAX77802_REG_32KHZ,
+ .clk_enable_mask = BIT(MAX77802_CLK_32K_CP),
+ },
+};
+
+static const struct
+max77686_hw_clk_info max77620_hw_clks_info[MAX77620_CLKS_NUM] = {
+ [MAX77620_CLK_32K_OUT0] = {
+ .name = "32khz_out0",
+ .clk_reg = MAX77620_REG_CNFG1_32K,
+ .clk_enable_mask = MAX77620_CNFG1_32K_OUT0_EN,
},
};
+static struct max77686_clk_init_data *to_max77686_clk_init_data(
+ struct clk_hw *hw)
+{
+ return container_of(hw, struct max77686_clk_init_data, hw);
+}
+
+static int max77686_clk_prepare(struct clk_hw *hw)
+{
+ struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
+
+ return regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg,
+ max77686->clk_info->clk_enable_mask,
+ max77686->clk_info->clk_enable_mask);
+}
+
+static void max77686_clk_unprepare(struct clk_hw *hw)
+{
+ struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
+
+ regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg,
+ max77686->clk_info->clk_enable_mask,
+ ~max77686->clk_info->clk_enable_mask);
+}
+
+static int max77686_clk_is_prepared(struct clk_hw *hw)
+{
+ struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(max77686->regmap, max77686->clk_info->clk_reg, &val);
+
+ if (ret < 0)
+ return -EINVAL;
+
+ return val & max77686->clk_info->clk_enable_mask;
+}
+
+static unsigned long max77686_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 32768;
+}
+
+static struct clk_ops max77686_clk_ops = {
+ .prepare = max77686_clk_prepare,
+ .unprepare = max77686_clk_unprepare,
+ .is_prepared = max77686_clk_is_prepared,
+ .recalc_rate = max77686_recalc_rate,
+};
+
+static struct clk_hw *
+of_clk_max77686_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct max77686_clk_driver_data *drv_data = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= drv_data->num_clks) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &drv_data->max_clk_data[idx].hw;
+}
+
static int max77686_clk_probe(struct platform_device *pdev)
{
- struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct max77686_clk_driver_data *drv_data;
+ const struct max77686_hw_clk_info *hw_clks;
+ struct regmap *regmap;
+ int i, ret, num_clks;
+
+ drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
+ if (!drv_data)
+ return -ENOMEM;
+
+ regmap = dev_get_regmap(parent, NULL);
+ if (!regmap) {
+ dev_err(dev, "Failed to get rtc regmap\n");
+ return -ENODEV;
+ }
+
+ drv_data->chip = id->driver_data;
+
+ switch (drv_data->chip) {
+ case CHIP_MAX77686:
+ num_clks = MAX77686_CLKS_NUM;
+ hw_clks = max77686_hw_clks_info;
+ break;
+
+ case CHIP_MAX77802:
+ num_clks = MAX77802_CLKS_NUM;
+ hw_clks = max77802_hw_clks_info;
+ break;
+
+ case CHIP_MAX77620:
+ num_clks = MAX77620_CLKS_NUM;
+ hw_clks = max77620_hw_clks_info;
+ break;
- return max_gen_clk_probe(pdev, iodev->regmap, MAX77686_REG_32KHZ,
- max77686_clks_init, MAX77686_CLKS_NUM);
+ default:
+ dev_err(dev, "Unknown Chip ID\n");
+ return -EINVAL;
+ }
+
+ drv_data->max_clk_data = devm_kcalloc(dev, num_clks,
+ sizeof(*drv_data->max_clk_data),
+ GFP_KERNEL);
+ if (!drv_data->max_clk_data)
+ return -ENOMEM;
+
+ for (i = 0; i < num_clks; i++) {
+ struct max77686_clk_init_data *max_clk_data;
+ const char *clk_name;
+
+ max_clk_data = &drv_data->max_clk_data[i];
+
+ max_clk_data->regmap = regmap;
+ max_clk_data->clk_info = &hw_clks[i];
+ max_clk_data->clk_idata.flags = hw_clks[i].flags;
+ max_clk_data->clk_idata.ops = &max77686_clk_ops;
+
+ if (parent->of_node &&
+ !of_property_read_string_index(parent->of_node,
+ "clock-output-names",
+ i, &clk_name))
+ max_clk_data->clk_idata.name = clk_name;
+ else
+ max_clk_data->clk_idata.name = hw_clks[i].name;
+
+ max_clk_data->hw.init = &max_clk_data->clk_idata;
+
+ ret = devm_clk_hw_register(dev, &max_clk_data->hw);
+ if (ret) {
+ dev_err(dev, "Failed to clock register: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_hw_register_clkdev(&max_clk_data->hw,
+ max_clk_data->clk_idata.name, NULL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to clkdev register: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (parent->of_node) {
+ ret = of_clk_add_hw_provider(parent->of_node, of_clk_max77686_get,
+ drv_data);
+
+ if (ret < 0) {
+ dev_err(dev, "Failed to register OF clock provider: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* MAX77802: Enable low-jitter mode on the 32khz clocks. */
+ if (drv_data->chip == CHIP_MAX77802) {
+ ret = regmap_update_bits(regmap, MAX77802_REG_32KHZ,
+ 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
+ 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
+ if (ret < 0) {
+ dev_err(dev, "Failed to config low-jitter: %d\n", ret);
+ goto remove_of_clk_provider;
+ }
+ }
+
+ return 0;
+
+remove_of_clk_provider:
+ if (parent->of_node)
+ of_clk_del_provider(parent->of_node);
+
+ return ret;
}
static int max77686_clk_remove(struct platform_device *pdev)
{
- return max_gen_clk_remove(pdev, MAX77686_CLKS_NUM);
+ struct device *parent = pdev->dev.parent;
+
+ if (parent->of_node)
+ of_clk_del_provider(parent->of_node);
+
+ return 0;
}
static const struct platform_device_id max77686_clk_id[] = {
- { "max77686-clk", 0},
- { },
+ { "max77686-clk", .driver_data = CHIP_MAX77686, },
+ { "max77802-clk", .driver_data = CHIP_MAX77802, },
+ { "max77620-clock", .driver_data = CHIP_MAX77620, },
+ {},
};
MODULE_DEVICE_TABLE(platform, max77686_clk_id);
diff --git a/drivers/clk/clk-max77802.c b/drivers/clk/clk-max77802.c
deleted file mode 100644
index 355dd2e..0000000
--- a/drivers/clk/clk-max77802.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * clk-max77802.c - Clock driver for Maxim 77802
- *
- * Copyright (C) 2014 Google, Inc
- *
- * Copyright (C) 2012 Samsung Electornics
- * Jonghwa Lee <jonghwa3.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This driver is based on clk-max77686.c
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/max77686-private.h>
-#include <linux/clk-provider.h>
-#include <linux/mutex.h>
-#include <linux/clkdev.h>
-
-#include <dt-bindings/clock/maxim,max77802.h>
-#include "clk-max-gen.h"
-
-#define MAX77802_CLOCK_OPMODE_MASK 0x1
-#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3
-
-static struct clk_init_data max77802_clks_init[MAX77802_CLKS_NUM] = {
- [MAX77802_CLK_32K_AP] = {
- .name = "32khz_ap",
- .ops = &max_gen_clk_ops,
- },
- [MAX77802_CLK_32K_CP] = {
- .name = "32khz_cp",
- .ops = &max_gen_clk_ops,
- },
-};
-
-static int max77802_clk_probe(struct platform_device *pdev)
-{
- struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- int ret;
-
- ret = max_gen_clk_probe(pdev, iodev->regmap, MAX77802_REG_32KHZ,
- max77802_clks_init, MAX77802_CLKS_NUM);
-
- if (ret) {
- dev_err(&pdev->dev, "generic probe failed %d\n", ret);
- return ret;
- }
-
- /* Enable low-jitter mode on the 32khz clocks. */
- ret = regmap_update_bits(iodev->regmap, MAX77802_REG_32KHZ,
- 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT,
- 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT);
- if (ret < 0)
- dev_err(&pdev->dev, "failed to enable low-jitter mode\n");
-
- return ret;
-}
-
-static int max77802_clk_remove(struct platform_device *pdev)
-{
- return max_gen_clk_remove(pdev, MAX77802_CLKS_NUM);
-}
-
-static const struct platform_device_id max77802_clk_id[] = {
- { "max77802-clk", 0},
- { },
-};
-MODULE_DEVICE_TABLE(platform, max77802_clk_id);
-
-static struct platform_driver max77802_clk_driver = {
- .driver = {
- .name = "max77802-clk",
- },
- .probe = max77802_clk_probe,
- .remove = max77802_clk_remove,
- .id_table = max77802_clk_id,
-};
-
-module_platform_driver(max77802_clk_driver);
-
-MODULE_DESCRIPTION("MAXIM 77802 Clock Driver");
-MODULE_AUTHOR("Javier Martinez Canillas <javier@osg.samsung.com");
-MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-mb86s7x.c b/drivers/clk/clk-mb86s7x.c
index e081775..2a83a3ff 100644
--- a/drivers/clk/clk-mb86s7x.c
+++ b/drivers/clk/clk-mb86s7x.c
@@ -327,10 +327,11 @@ static struct clk_ops clk_clc_ops = {
.set_rate = clc_set_rate,
};
-struct clk *mb86s7x_clclk_register(struct device *cpu_dev)
+static struct clk_hw *mb86s7x_clclk_register(struct device *cpu_dev)
{
struct clk_init_data init;
struct cl_clk *clc;
+ int ret;
clc = kzalloc(sizeof(*clc), GFP_KERNEL);
if (!clc)
@@ -344,14 +345,17 @@ struct clk *mb86s7x_clclk_register(struct device *cpu_dev)
init.flags = CLK_GET_RATE_NOCACHE;
init.num_parents = 0;
- return devm_clk_register(cpu_dev, &clc->hw);
+ ret = devm_clk_hw_register(cpu_dev, &clc->hw);
+ if (ret)
+ return ERR_PTR(ret);
+ return &clc->hw;
}
static int mb86s7x_clclk_of_init(void)
{
int cpu, ret = -ENODEV;
struct device_node *np;
- struct clk *clk;
+ struct clk_hw *hw;
np = of_find_compatible_node(NULL, NULL, "fujitsu,mb86s70-scb-1.0");
if (!np || !of_device_is_available(np))
@@ -365,12 +369,12 @@ static int mb86s7x_clclk_of_init(void)
continue;
}
- clk = mb86s7x_clclk_register(cpu_dev);
- if (IS_ERR(clk)) {
+ hw = mb86s7x_clclk_register(cpu_dev);
+ if (IS_ERR(hw)) {
pr_err("failed to register cpu%d clock\n", cpu);
continue;
}
- if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) {
+ if (clk_hw_register_clkdev(hw, NULL, dev_name(cpu_dev))) {
pr_err("failed to register cpu%d clock lookup\n", cpu);
continue;
}
diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c
index f37f719..b86dac8 100644
--- a/drivers/clk/clk-moxart.c
+++ b/drivers/clk/clk-moxart.c
@@ -19,7 +19,8 @@
static void __init moxart_of_pll_clk_init(struct device_node *node)
{
static void __iomem *base;
- struct clk *clk, *ref_clk;
+ struct clk_hw *hw;
+ struct clk *ref_clk;
unsigned int mul;
const char *name = node->name;
const char *parent_name;
@@ -42,14 +43,14 @@ static void __init moxart_of_pll_clk_init(struct device_node *node)
return;
}
- clk = clk_register_fixed_factor(NULL, name, parent_name, 0, mul, 1);
- if (IS_ERR(clk)) {
+ hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1);
+ if (IS_ERR(hw)) {
pr_err("%s: failed to register clock\n", node->full_name);
return;
}
- clk_register_clkdev(clk, NULL, name);
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_hw_register_clkdev(hw, NULL, name);
+ of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock",
moxart_of_pll_clk_init);
@@ -57,7 +58,8 @@ CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock",
static void __init moxart_of_apb_clk_init(struct device_node *node)
{
static void __iomem *base;
- struct clk *clk, *pll_clk;
+ struct clk_hw *hw;
+ struct clk *pll_clk;
unsigned int div, val;
unsigned int div_idx[] = { 2, 3, 4, 6, 8};
const char *name = node->name;
@@ -85,14 +87,14 @@ static void __init moxart_of_apb_clk_init(struct device_node *node)
return;
}
- clk = clk_register_fixed_factor(NULL, name, parent_name, 0, 1, div);
- if (IS_ERR(clk)) {
+ hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div);
+ if (IS_ERR(hw)) {
pr_err("%s: failed to register clock\n", node->full_name);
return;
}
- clk_register_clkdev(clk, NULL, name);
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_hw_register_clkdev(hw, NULL, name);
+ of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
}
CLK_OF_DECLARE(moxart_apb_clock, "moxa,moxart-apb-clock",
moxart_of_apb_clk_init);
diff --git a/drivers/clk/clk-nspire.c b/drivers/clk/clk-nspire.c
index 64f196a..f861011 100644
--- a/drivers/clk/clk-nspire.c
+++ b/drivers/clk/clk-nspire.c
@@ -69,7 +69,7 @@ static void __init nspire_ahbdiv_setup(struct device_node *node,
{
u32 val;
void __iomem *io;
- struct clk *clk;
+ struct clk_hw *hw;
const char *clk_name = node->name;
const char *parent_name;
struct nspire_clk_info info;
@@ -85,10 +85,10 @@ static void __init nspire_ahbdiv_setup(struct device_node *node,
of_property_read_string(node, "clock-output-names", &clk_name);
parent_name = of_clk_get_parent_name(node, 0);
- clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0,
- 1, info.base_ahb_ratio);
- if (!IS_ERR(clk))
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ hw = clk_hw_register_fixed_factor(NULL, clk_name, parent_name, 0,
+ 1, info.base_ahb_ratio);
+ if (!IS_ERR(hw))
+ of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
}
static void __init nspire_ahbdiv_setup_cx(struct device_node *node)
@@ -111,7 +111,7 @@ static void __init nspire_clk_setup(struct device_node *node,
{
u32 val;
void __iomem *io;
- struct clk *clk;
+ struct clk_hw *hw;
const char *clk_name = node->name;
struct nspire_clk_info info;
@@ -125,9 +125,10 @@ static void __init nspire_clk_setup(struct device_node *node,
of_property_read_string(node, "clock-output-names", &clk_name);
- clk = clk_register_fixed_rate(NULL, clk_name, NULL, 0, info.base_clock);
- if (!IS_ERR(clk))
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ hw = clk_hw_register_fixed_rate(NULL, clk_name, NULL, 0,
+ info.base_clock);
+ if (!IS_ERR(hw))
+ of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
else
return;
diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c
index 8328863..31f590c 100644
--- a/drivers/clk/clk-palmas.c
+++ b/drivers/clk/clk-palmas.c
@@ -41,7 +41,6 @@ struct palmas_clk32k_desc {
struct palmas_clock_info {
struct device *dev;
- struct clk *clk;
struct clk_hw hw;
struct palmas *palmas;
const struct palmas_clk32k_desc *clk_desc;
@@ -218,7 +217,7 @@ static int palmas_clks_init_configure(struct palmas_clock_info *cinfo)
}
if (cinfo->ext_control_pin) {
- ret = clk_prepare(cinfo->clk);
+ ret = clk_prepare(cinfo->hw.clk);
if (ret < 0) {
dev_err(cinfo->dev, "Clock prep failed, %d\n", ret);
return ret;
@@ -242,7 +241,6 @@ static int palmas_clks_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
const struct palmas_clks_of_match_data *match_data;
struct palmas_clock_info *cinfo;
- struct clk *clk;
int ret;
match_data = of_device_get_match_data(&pdev->dev);
@@ -261,22 +259,20 @@ static int palmas_clks_probe(struct platform_device *pdev)
cinfo->clk_desc = &match_data->desc;
cinfo->hw.init = &match_data->init;
- clk = devm_clk_register(&pdev->dev, &cinfo->hw);
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
+ ret = devm_clk_hw_register(&pdev->dev, &cinfo->hw);
+ if (ret) {
dev_err(&pdev->dev, "Fail to register clock %s, %d\n",
match_data->desc.clk_name, ret);
return ret;
}
- cinfo->clk = clk;
ret = palmas_clks_init_configure(cinfo);
if (ret < 0) {
dev_err(&pdev->dev, "Clock config failed, %d\n", ret);
return ret;
}
- ret = of_clk_add_provider(node, of_clk_src_simple_get, cinfo->clk);
+ ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &cinfo->hw);
if (ret < 0)
dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret);
return ret;
diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c
index 1630a1f..8cb9d11 100644
--- a/drivers/clk/clk-pwm.c
+++ b/drivers/clk/clk-pwm.c
@@ -61,7 +61,6 @@ static int clk_pwm_probe(struct platform_device *pdev)
struct pwm_device *pwm;
struct pwm_args pargs;
const char *clk_name;
- struct clk *clk;
int ret;
clk_pwm = devm_kzalloc(&pdev->dev, sizeof(*clk_pwm), GFP_KERNEL);
@@ -107,11 +106,11 @@ static int clk_pwm_probe(struct platform_device *pdev)
clk_pwm->pwm = pwm;
clk_pwm->hw.init = &init;
- clk = devm_clk_register(&pdev->dev, &clk_pwm->hw);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ ret = devm_clk_hw_register(&pdev->dev, &clk_pwm->hw);
+ if (ret)
+ return ret;
- return of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return of_clk_add_hw_provider(node, of_clk_hw_simple_get, &clk_pwm->hw);
}
static int clk_pwm_remove(struct platform_device *pdev)
diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c
index 58566a17..20b1055 100644
--- a/drivers/clk/clk-qoriq.c
+++ b/drivers/clk/clk-qoriq.c
@@ -766,7 +766,11 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx)
if (!hwc)
return NULL;
- hwc->reg = cg->regs + 0x20 * idx;
+ if (cg->info.flags & CG_VER3)
+ hwc->reg = cg->regs + 0x70000 + 0x20 * idx;
+ else
+ hwc->reg = cg->regs + 0x20 * idx;
+
hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]];
/*
diff --git a/drivers/clk/clk-rk808.c b/drivers/clk/clk-rk808.c
index 7438303..6461f28 100644
--- a/drivers/clk/clk-rk808.c
+++ b/drivers/clk/clk-rk808.c
@@ -22,11 +22,8 @@
#include <linux/mfd/rk808.h>
#include <linux/i2c.h>
-#define RK808_NR_OUTPUT 2
-
struct rk808_clkout {
struct rk808 *rk808;
- struct clk_onecell_data clk_data;
struct clk_hw clkout1_hw;
struct clk_hw clkout2_hw;
};
@@ -85,14 +82,28 @@ static const struct clk_ops rk808_clkout2_ops = {
.recalc_rate = rk808_clkout_recalc_rate,
};
+static struct clk_hw *
+of_clk_rk808_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct rk808_clkout *rk808_clkout = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= 2) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return idx ? &rk808_clkout->clkout2_hw : &rk808_clkout->clkout1_hw;
+}
+
static int rk808_clkout_probe(struct platform_device *pdev)
{
struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
struct i2c_client *client = rk808->i2c;
struct device_node *node = client->dev.of_node;
struct clk_init_data init = {};
- struct clk **clk_table;
struct rk808_clkout *rk808_clkout;
+ int ret;
rk808_clkout = devm_kzalloc(&client->dev,
sizeof(*rk808_clkout), GFP_KERNEL);
@@ -101,11 +112,6 @@ static int rk808_clkout_probe(struct platform_device *pdev)
rk808_clkout->rk808 = rk808;
- clk_table = devm_kcalloc(&client->dev, RK808_NR_OUTPUT,
- sizeof(struct clk *), GFP_KERNEL);
- if (!clk_table)
- return -ENOMEM;
-
init.parent_names = NULL;
init.num_parents = 0;
init.name = "rk808-clkout1";
@@ -116,10 +122,9 @@ static int rk808_clkout_probe(struct platform_device *pdev)
of_property_read_string_index(node, "clock-output-names",
0, &init.name);
- clk_table[0] = devm_clk_register(&client->dev,
- &rk808_clkout->clkout1_hw);
- if (IS_ERR(clk_table[0]))
- return PTR_ERR(clk_table[0]);
+ ret = devm_clk_hw_register(&client->dev, &rk808_clkout->clkout1_hw);
+ if (ret)
+ return ret;
init.name = "rk808-clkout2";
init.ops = &rk808_clkout2_ops;
@@ -129,16 +134,11 @@ static int rk808_clkout_probe(struct platform_device *pdev)
of_property_read_string_index(node, "clock-output-names",
1, &init.name);
- clk_table[1] = devm_clk_register(&client->dev,
- &rk808_clkout->clkout2_hw);
- if (IS_ERR(clk_table[1]))
- return PTR_ERR(clk_table[1]);
-
- rk808_clkout->clk_data.clks = clk_table;
- rk808_clkout->clk_data.clk_num = RK808_NR_OUTPUT;
+ ret = devm_clk_hw_register(&client->dev, &rk808_clkout->clkout2_hw);
+ if (ret)
+ return ret;
- return of_clk_add_provider(node, of_clk_src_onecell_get,
- &rk808_clkout->clk_data);
+ return of_clk_add_hw_provider(node, of_clk_rk808_get, rk808_clkout);
}
static int rk808_clkout_remove(struct platform_device *pdev)
diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c
index 6962ee5..2a3e9d8 100644
--- a/drivers/clk/clk-scpi.c
+++ b/drivers/clk/clk-scpi.c
@@ -146,13 +146,13 @@ static const struct of_device_id scpi_clk_match[] = {
{}
};
-static struct clk *
+static int
scpi_clk_ops_init(struct device *dev, const struct of_device_id *match,
struct scpi_clk *sclk, const char *name)
{
struct clk_init_data init;
- struct clk *clk;
unsigned long min = 0, max = 0;
+ int ret;
init.name = name;
init.flags = 0;
@@ -164,18 +164,18 @@ scpi_clk_ops_init(struct device *dev, const struct of_device_id *match,
if (init.ops == &scpi_dvfs_ops) {
sclk->info = sclk->scpi_ops->dvfs_get_info(sclk->id);
if (IS_ERR(sclk->info))
- return NULL;
+ return PTR_ERR(sclk->info);
} else if (init.ops == &scpi_clk_ops) {
if (sclk->scpi_ops->clk_get_range(sclk->id, &min, &max) || !max)
- return NULL;
+ return -EINVAL;
} else {
- return NULL;
+ return -EINVAL;
}
- clk = devm_clk_register(dev, &sclk->hw);
- if (!IS_ERR(clk) && max)
+ ret = devm_clk_hw_register(dev, &sclk->hw);
+ if (!ret && max)
clk_hw_set_rate_range(&sclk->hw, min, max);
- return clk;
+ return ret;
}
struct scpi_clk_data {
@@ -183,7 +183,7 @@ struct scpi_clk_data {
unsigned int clk_num;
};
-static struct clk *
+static struct clk_hw *
scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data)
{
struct scpi_clk *sclk;
@@ -193,7 +193,7 @@ scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data)
for (count = 0; count < clk_data->clk_num; count++) {
sclk = clk_data->clk[count];
if (idx == sclk->id)
- return sclk->hw.clk;
+ return &sclk->hw;
}
return ERR_PTR(-EINVAL);
@@ -202,8 +202,7 @@ scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data)
static int scpi_clk_add(struct device *dev, struct device_node *np,
const struct of_device_id *match)
{
- struct clk **clks;
- int idx, count;
+ int idx, count, err;
struct scpi_clk_data *clk_data;
count = of_property_count_strings(np, "clock-output-names");
@@ -222,10 +221,6 @@ static int scpi_clk_add(struct device *dev, struct device_node *np,
if (!clk_data->clk)
return -ENOMEM;
- clks = devm_kcalloc(dev, count, sizeof(*clks), GFP_KERNEL);
- if (!clks)
- return -ENOMEM;
-
for (idx = 0; idx < count; idx++) {
struct scpi_clk *sclk;
const char *name;
@@ -249,15 +244,15 @@ static int scpi_clk_add(struct device *dev, struct device_node *np,
sclk->id = val;
- clks[idx] = scpi_clk_ops_init(dev, match, sclk, name);
- if (IS_ERR_OR_NULL(clks[idx]))
+ err = scpi_clk_ops_init(dev, match, sclk, name);
+ if (err)
dev_err(dev, "failed to register clock '%s'\n", name);
else
dev_dbg(dev, "Registered clock '%s'\n", name);
clk_data->clk[idx] = sclk;
}
- return of_clk_add_provider(np, scpi_of_clk_src_get, clk_data);
+ return of_clk_add_hw_provider(np, scpi_of_clk_src_get, clk_data);
}
static int scpi_clocks_remove(struct platform_device *pdev)
diff --git a/drivers/clk/clk-si514.c b/drivers/clk/clk-si514.c
index ceef25b..09b6718 100644
--- a/drivers/clk/clk-si514.c
+++ b/drivers/clk/clk-si514.c
@@ -305,7 +305,6 @@ static int si514_probe(struct i2c_client *client,
{
struct clk_si514 *data;
struct clk_init_data init;
- struct clk *clk;
int err;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
@@ -330,13 +329,13 @@ static int si514_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);
- clk = devm_clk_register(&client->dev, &data->hw);
- if (IS_ERR(clk)) {
+ err = devm_clk_hw_register(&client->dev, &data->hw);
+ if (err) {
dev_err(&client->dev, "clock registration failed\n");
- return PTR_ERR(clk);
+ return err;
}
- err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get,
- clk);
+ err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get,
+ &data->hw);
if (err) {
dev_err(&client->dev, "unable to add clk provider\n");
return err;
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c
index b1bc12c..b051db4 100644
--- a/drivers/clk/clk-si5351.c
+++ b/drivers/clk/clk-si5351.c
@@ -54,7 +54,6 @@ struct si5351_driver_data {
enum si5351_variant variant;
struct i2c_client *client;
struct regmap *regmap;
- struct clk_onecell_data onecell;
struct clk *pxtal;
const char *pxtal_name;
@@ -66,6 +65,7 @@ struct si5351_driver_data {
struct si5351_hw_data pll[2];
struct si5351_hw_data *msynth;
struct si5351_hw_data *clkout;
+ size_t num_clkout;
};
static const char * const si5351_input_names[] = {
@@ -1307,11 +1307,31 @@ put_child:
of_node_put(child);
return -EINVAL;
}
+
+static struct clk_hw *
+si53351_of_clk_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct si5351_driver_data *drvdata = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= drvdata->num_clkout) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &drvdata->clkout[idx].hw;
+}
#else
static int si5351_dt_parse(struct i2c_client *client, enum si5351_variant variant)
{
return 0;
}
+
+static struct clk_hw *
+si53351_of_clk_get(struct of_phandle_args *clkspec, void *data)
+{
+ return NULL;
+}
#endif /* CONFIG_OF */
static int si5351_i2c_probe(struct i2c_client *client,
@@ -1321,7 +1341,6 @@ static int si5351_i2c_probe(struct i2c_client *client,
struct si5351_platform_data *pdata;
struct si5351_driver_data *drvdata;
struct clk_init_data init;
- struct clk *clk;
const char *parent_names[4];
u8 num_parents, num_clocks;
int ret, n;
@@ -1438,10 +1457,9 @@ static int si5351_i2c_probe(struct i2c_client *client,
init.num_parents = 1;
}
drvdata->xtal.init = &init;
- clk = devm_clk_register(&client->dev, &drvdata->xtal);
- if (IS_ERR(clk)) {
+ ret = devm_clk_hw_register(&client->dev, &drvdata->xtal);
+ if (ret) {
dev_err(&client->dev, "unable to register %s\n", init.name);
- ret = PTR_ERR(clk);
goto err_clk;
}
@@ -1456,11 +1474,10 @@ static int si5351_i2c_probe(struct i2c_client *client,
init.num_parents = 1;
}
drvdata->clkin.init = &init;
- clk = devm_clk_register(&client->dev, &drvdata->clkin);
- if (IS_ERR(clk)) {
+ ret = devm_clk_hw_register(&client->dev, &drvdata->clkin);
+ if (ret) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
- ret = PTR_ERR(clk);
goto err_clk;
}
}
@@ -1480,10 +1497,9 @@ static int si5351_i2c_probe(struct i2c_client *client,
init.flags = 0;
init.parent_names = parent_names;
init.num_parents = num_parents;
- clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw);
- if (IS_ERR(clk)) {
+ ret = devm_clk_hw_register(&client->dev, &drvdata->pll[0].hw);
+ if (ret) {
dev_err(&client->dev, "unable to register %s\n", init.name);
- ret = PTR_ERR(clk);
goto err_clk;
}
@@ -1505,10 +1521,9 @@ static int si5351_i2c_probe(struct i2c_client *client,
init.parent_names = parent_names;
init.num_parents = num_parents;
}
- clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw);
- if (IS_ERR(clk)) {
+ ret = devm_clk_hw_register(&client->dev, &drvdata->pll[1].hw);
+ if (ret) {
dev_err(&client->dev, "unable to register %s\n", init.name);
- ret = PTR_ERR(clk);
goto err_clk;
}
@@ -1524,13 +1539,9 @@ static int si5351_i2c_probe(struct i2c_client *client,
sizeof(*drvdata->msynth), GFP_KERNEL);
drvdata->clkout = devm_kzalloc(&client->dev, num_clocks *
sizeof(*drvdata->clkout), GFP_KERNEL);
+ drvdata->num_clkout = num_clocks;
- drvdata->onecell.clk_num = num_clocks;
- drvdata->onecell.clks = devm_kzalloc(&client->dev,
- num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL);
-
- if (WARN_ON(!drvdata->msynth || !drvdata->clkout ||
- !drvdata->onecell.clks)) {
+ if (WARN_ON(!drvdata->msynth || !drvdata->clkout)) {
ret = -ENOMEM;
goto err_clk;
}
@@ -1547,11 +1558,11 @@ static int si5351_i2c_probe(struct i2c_client *client,
init.flags |= CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
init.num_parents = 2;
- clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw);
- if (IS_ERR(clk)) {
+ ret = devm_clk_hw_register(&client->dev,
+ &drvdata->msynth[n].hw);
+ if (ret) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
- ret = PTR_ERR(clk);
goto err_clk;
}
}
@@ -1575,19 +1586,19 @@ static int si5351_i2c_probe(struct i2c_client *client,
init.flags |= CLK_SET_RATE_PARENT;
init.parent_names = parent_names;
init.num_parents = num_parents;
- clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw);
- if (IS_ERR(clk)) {
+ ret = devm_clk_hw_register(&client->dev,
+ &drvdata->clkout[n].hw);
+ if (ret) {
dev_err(&client->dev, "unable to register %s\n",
init.name);
- ret = PTR_ERR(clk);
goto err_clk;
}
- drvdata->onecell.clks[n] = clk;
/* set initial clkout rate */
if (pdata->clkout[n].rate != 0) {
int ret;
- ret = clk_set_rate(clk, pdata->clkout[n].rate);
+ ret = clk_set_rate(drvdata->clkout[n].hw.clk,
+ pdata->clkout[n].rate);
if (ret != 0) {
dev_err(&client->dev, "Cannot set rate : %d\n",
ret);
@@ -1595,8 +1606,8 @@ static int si5351_i2c_probe(struct i2c_client *client,
}
}
- ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get,
- &drvdata->onecell);
+ ret = of_clk_add_hw_provider(client->dev.of_node, si53351_of_clk_get,
+ drvdata);
if (ret) {
dev_err(&client->dev, "unable to add clk provider\n");
goto err_clk;
diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c
index d566485..646af1d 100644
--- a/drivers/clk/clk-si570.c
+++ b/drivers/clk/clk-si570.c
@@ -408,7 +408,6 @@ static int si570_probe(struct i2c_client *client,
{
struct clk_si570 *data;
struct clk_init_data init;
- struct clk *clk;
u32 initial_fout, factory_fout, stability;
int err;
enum clk_si570_variant variant = id->driver_data;
@@ -462,13 +461,13 @@ static int si570_probe(struct i2c_client *client,
if (err)
return err;
- clk = devm_clk_register(&client->dev, &data->hw);
- if (IS_ERR(clk)) {
+ err = devm_clk_hw_register(&client->dev, &data->hw);
+ if (err) {
dev_err(&client->dev, "clock registration failed\n");
- return PTR_ERR(clk);
+ return err;
}
- err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get,
- clk);
+ err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get,
+ &data->hw);
if (err) {
dev_err(&client->dev, "unable to add clk provider\n");
return err;
@@ -477,7 +476,7 @@ static int si570_probe(struct i2c_client *client,
/* Read the requested initial output frequency from device tree */
if (!of_property_read_u32(client->dev.of_node, "clock-frequency",
&initial_fout)) {
- err = clk_set_rate(clk, initial_fout);
+ err = clk_set_rate(data->hw.clk, initial_fout);
if (err) {
of_clk_del_provider(client->dev.of_node);
return err;
diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c
index 697c667..7b222a5 100644
--- a/drivers/clk/clk-twl6040.c
+++ b/drivers/clk/clk-twl6040.c
@@ -26,60 +26,73 @@
#include <linux/mfd/twl6040.h>
#include <linux/clk-provider.h>
-struct twl6040_clk {
+struct twl6040_pdmclk {
struct twl6040 *twl6040;
struct device *dev;
- struct clk_hw mcpdm_fclk;
- struct clk *clk;
+ struct clk_hw pdmclk_hw;
int enabled;
};
-static int twl6040_bitclk_is_enabled(struct clk_hw *hw)
+static int twl6040_pdmclk_is_prepared(struct clk_hw *hw)
{
- struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
- mcpdm_fclk);
- return twl6040_clk->enabled;
+ struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
+ pdmclk_hw);
+
+ return pdmclk->enabled;
}
-static int twl6040_bitclk_prepare(struct clk_hw *hw)
+static int twl6040_pdmclk_prepare(struct clk_hw *hw)
{
- struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
- mcpdm_fclk);
+ struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
+ pdmclk_hw);
int ret;
- ret = twl6040_power(twl6040_clk->twl6040, 1);
+ ret = twl6040_power(pdmclk->twl6040, 1);
if (!ret)
- twl6040_clk->enabled = 1;
+ pdmclk->enabled = 1;
return ret;
}
-static void twl6040_bitclk_unprepare(struct clk_hw *hw)
+static void twl6040_pdmclk_unprepare(struct clk_hw *hw)
{
- struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
- mcpdm_fclk);
+ struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
+ pdmclk_hw);
int ret;
- ret = twl6040_power(twl6040_clk->twl6040, 0);
+ ret = twl6040_power(pdmclk->twl6040, 0);
if (!ret)
- twl6040_clk->enabled = 0;
+ pdmclk->enabled = 0;
+
}
-static const struct clk_ops twl6040_mcpdm_ops = {
- .is_enabled = twl6040_bitclk_is_enabled,
- .prepare = twl6040_bitclk_prepare,
- .unprepare = twl6040_bitclk_unprepare,
+static unsigned long twl6040_pdmclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct twl6040_pdmclk *pdmclk = container_of(hw, struct twl6040_pdmclk,
+ pdmclk_hw);
+
+ return twl6040_get_sysclk(pdmclk->twl6040);
+}
+
+static const struct clk_ops twl6040_pdmclk_ops = {
+ .is_prepared = twl6040_pdmclk_is_prepared,
+ .prepare = twl6040_pdmclk_prepare,
+ .unprepare = twl6040_pdmclk_unprepare,
+ .recalc_rate = twl6040_pdmclk_recalc_rate,
};
-static struct clk_init_data wm831x_clkout_init = {
- .name = "mcpdm_fclk",
- .ops = &twl6040_mcpdm_ops,
+static struct clk_init_data twl6040_pdmclk_init = {
+ .name = "pdmclk",
+ .ops = &twl6040_pdmclk_ops,
+ .flags = CLK_GET_RATE_NOCACHE,
};
-static int twl6040_clk_probe(struct platform_device *pdev)
+static int twl6040_pdmclk_probe(struct platform_device *pdev)
{
struct twl6040 *twl6040 = dev_get_drvdata(pdev->dev.parent);
- struct twl6040_clk *clkdata;
+ struct twl6040_pdmclk *clkdata;
+ int ret;
clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
if (!clkdata)
@@ -88,26 +101,28 @@ static int twl6040_clk_probe(struct platform_device *pdev)
clkdata->dev = &pdev->dev;
clkdata->twl6040 = twl6040;
- clkdata->mcpdm_fclk.init = &wm831x_clkout_init;
- clkdata->clk = devm_clk_register(&pdev->dev, &clkdata->mcpdm_fclk);
- if (IS_ERR(clkdata->clk))
- return PTR_ERR(clkdata->clk);
+ clkdata->pdmclk_hw.init = &twl6040_pdmclk_init;
+ ret = devm_clk_hw_register(&pdev->dev, &clkdata->pdmclk_hw);
+ if (ret)
+ return ret;
platform_set_drvdata(pdev, clkdata);
- return 0;
+ return of_clk_add_hw_provider(pdev->dev.parent->of_node,
+ of_clk_hw_simple_get,
+ &clkdata->pdmclk_hw);
}
-static struct platform_driver twl6040_clk_driver = {
+static struct platform_driver twl6040_pdmclk_driver = {
.driver = {
- .name = "twl6040-clk",
+ .name = "twl6040-pdmclk",
},
- .probe = twl6040_clk_probe,
+ .probe = twl6040_pdmclk_probe,
};
-module_platform_driver(twl6040_clk_driver);
+module_platform_driver(twl6040_pdmclk_driver);
MODULE_DESCRIPTION("TWL6040 clock driver for McPDM functional clock");
MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
-MODULE_ALIAS("platform:twl6040-clk");
+MODULE_ALIAS("platform:twl6040-pdmclk");
MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
index 37368a3..4161a6f 100644
--- a/drivers/clk/clk-vt8500.c
+++ b/drivers/clk/clk-vt8500.c
@@ -232,7 +232,7 @@ static const struct clk_ops vt8500_gated_divisor_clk_ops = {
static __init void vtwm_device_clk_init(struct device_node *node)
{
u32 en_reg, div_reg;
- struct clk *clk;
+ struct clk_hw *hw;
struct clk_device *dev_clk;
const char *clk_name = node->name;
const char *parent_name;
@@ -301,13 +301,14 @@ static __init void vtwm_device_clk_init(struct device_node *node)
dev_clk->hw.init = &init;
- clk = clk_register(NULL, &dev_clk->hw);
- if (WARN_ON(IS_ERR(clk))) {
+ hw = &dev_clk->hw;
+ rc = clk_hw_register(NULL, hw);
+ if (WARN_ON(rc)) {
kfree(dev_clk);
return;
}
- rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
- clk_register_clkdev(clk, clk_name, NULL);
+ rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
+ clk_hw_register_clkdev(hw, clk_name, NULL);
}
CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init);
@@ -681,7 +682,7 @@ static const struct clk_ops vtwm_pll_ops = {
static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type)
{
u32 reg;
- struct clk *clk;
+ struct clk_hw *hw;
struct clk_pll *pll_clk;
const char *clk_name = node->name;
const char *parent_name;
@@ -714,13 +715,14 @@ static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type)
pll_clk->hw.init = &init;
- clk = clk_register(NULL, &pll_clk->hw);
- if (WARN_ON(IS_ERR(clk))) {
+ hw = &pll_clk->hw;
+ rc = clk_hw_register(NULL, &pll_clk->hw);
+ if (WARN_ON(rc)) {
kfree(pll_clk);
return;
}
- rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
- clk_register_clkdev(clk, clk_name, NULL);
+ rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
+ clk_hw_register_clkdev(hw, clk_name, NULL);
}
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
index 88def4b..f4fdac5 100644
--- a/drivers/clk/clk-wm831x.c
+++ b/drivers/clk/clk-wm831x.c
@@ -24,9 +24,6 @@ struct wm831x_clk {
struct clk_hw xtal_hw;
struct clk_hw fll_hw;
struct clk_hw clkout_hw;
- struct clk *xtal;
- struct clk *fll;
- struct clk *clkout;
bool xtal_ena;
};
@@ -370,19 +367,19 @@ static int wm831x_clk_probe(struct platform_device *pdev)
clkdata->xtal_ena = ret & WM831X_XTAL_ENA;
clkdata->xtal_hw.init = &wm831x_xtal_init;
- clkdata->xtal = devm_clk_register(&pdev->dev, &clkdata->xtal_hw);
- if (IS_ERR(clkdata->xtal))
- return PTR_ERR(clkdata->xtal);
+ ret = devm_clk_hw_register(&pdev->dev, &clkdata->xtal_hw);
+ if (ret)
+ return ret;
clkdata->fll_hw.init = &wm831x_fll_init;
- clkdata->fll = devm_clk_register(&pdev->dev, &clkdata->fll_hw);
- if (IS_ERR(clkdata->fll))
- return PTR_ERR(clkdata->fll);
+ ret = devm_clk_hw_register(&pdev->dev, &clkdata->fll_hw);
+ if (ret)
+ return ret;
clkdata->clkout_hw.init = &wm831x_clkout_init;
- clkdata->clkout = devm_clk_register(&pdev->dev, &clkdata->clkout_hw);
- if (IS_ERR(clkdata->clkout))
- return PTR_ERR(clkdata->clkout);
+ ret = devm_clk_hw_register(&pdev->dev, &clkdata->clkout_hw);
+ if (ret)
+ return ret;
platform_set_drvdata(pdev, clkdata);
diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c
index 3433132..5daddf5 100644
--- a/drivers/clk/clk-xgene.c
+++ b/drivers/clk/clk-xgene.c
@@ -217,6 +217,226 @@ static void xgene_pcppllclk_init(struct device_node *np)
xgene_pllclk_init(np, PLL_TYPE_PCP);
}
+/**
+ * struct xgene_clk_pmd - PMD clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @reg: register containing the fractional scale multiplier (scaler)
+ * @shift: shift to the unit bit field
+ * @denom: 1/denominator unit
+ * @lock: register lock
+ * Flags:
+ * XGENE_CLK_PMD_SCALE_INVERTED - By default the scaler is the value read
+ * from the register plus one. For example,
+ * 0 for (0 + 1) / denom,
+ * 1 for (1 + 1) / denom and etc.
+ * If this flag is set, it is
+ * 0 for (denom - 0) / denom,
+ * 1 for (denom - 1) / denom and etc.
+ *
+ */
+struct xgene_clk_pmd {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 shift;
+ u32 mask;
+ u64 denom;
+ u32 flags;
+ spinlock_t *lock;
+};
+
+#define to_xgene_clk_pmd(_hw) container_of(_hw, struct xgene_clk_pmd, hw)
+
+#define XGENE_CLK_PMD_SCALE_INVERTED BIT(0)
+#define XGENE_CLK_PMD_SHIFT 8
+#define XGENE_CLK_PMD_WIDTH 3
+
+static unsigned long xgene_clk_pmd_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw);
+ unsigned long flags = 0;
+ u64 ret, scale;
+ u32 val;
+
+ if (fd->lock)
+ spin_lock_irqsave(fd->lock, flags);
+ else
+ __acquire(fd->lock);
+
+ val = clk_readl(fd->reg);
+
+ if (fd->lock)
+ spin_unlock_irqrestore(fd->lock, flags);
+ else
+ __release(fd->lock);
+
+ ret = (u64)parent_rate;
+
+ scale = (val & fd->mask) >> fd->shift;
+ if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED)
+ scale = fd->denom - scale;
+ else
+ scale++;
+
+ /* freq = parent_rate * scaler / denom */
+ do_div(ret, fd->denom);
+ ret *= scale;
+ if (ret == 0)
+ ret = (u64)parent_rate;
+
+ return ret;
+}
+
+static long xgene_clk_pmd_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw);
+ u64 ret, scale;
+
+ if (!rate || rate >= *parent_rate)
+ return *parent_rate;
+
+ /* freq = parent_rate * scaler / denom */
+ ret = rate * fd->denom;
+ scale = DIV_ROUND_UP_ULL(ret, *parent_rate);
+
+ ret = (u64)*parent_rate * scale;
+ do_div(ret, fd->denom);
+
+ return ret;
+}
+
+static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct xgene_clk_pmd *fd = to_xgene_clk_pmd(hw);
+ unsigned long flags = 0;
+ u64 scale, ret;
+ u32 val;
+
+ /*
+ * Compute the scaler:
+ *
+ * freq = parent_rate * scaler / denom, or
+ * scaler = freq * denom / parent_rate
+ */
+ ret = rate * fd->denom;
+ scale = DIV_ROUND_UP_ULL(ret, (u64)parent_rate);
+
+ /* Check if inverted */
+ if (fd->flags & XGENE_CLK_PMD_SCALE_INVERTED)
+ scale = fd->denom - scale;
+ else
+ scale--;
+
+ if (fd->lock)
+ spin_lock_irqsave(fd->lock, flags);
+ else
+ __acquire(fd->lock);
+
+ val = clk_readl(fd->reg);
+ val &= ~fd->mask;
+ val |= (scale << fd->shift);
+ clk_writel(val, fd->reg);
+
+ if (fd->lock)
+ spin_unlock_irqrestore(fd->lock, flags);
+ else
+ __release(fd->lock);
+
+ return 0;
+}
+
+static const struct clk_ops xgene_clk_pmd_ops = {
+ .recalc_rate = xgene_clk_pmd_recalc_rate,
+ .round_rate = xgene_clk_pmd_round_rate,
+ .set_rate = xgene_clk_pmd_set_rate,
+};
+
+static struct clk *
+xgene_register_clk_pmd(struct device *dev,
+ const char *name, const char *parent_name,
+ unsigned long flags, void __iomem *reg, u8 shift,
+ u8 width, u64 denom, u32 clk_flags, spinlock_t *lock)
+{
+ struct xgene_clk_pmd *fd;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ fd = kzalloc(sizeof(*fd), GFP_KERNEL);
+ if (!fd)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &xgene_clk_pmd_ops;
+ init.flags = flags;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+
+ fd->reg = reg;
+ fd->shift = shift;
+ fd->mask = (BIT(width) - 1) << shift;
+ fd->denom = denom;
+ fd->flags = clk_flags;
+ fd->lock = lock;
+ fd->hw.init = &init;
+
+ clk = clk_register(dev, &fd->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: could not register clk %s\n", __func__, name);
+ kfree(fd);
+ return NULL;
+ }
+
+ return clk;
+}
+
+static void xgene_pmdclk_init(struct device_node *np)
+{
+ const char *clk_name = np->full_name;
+ void __iomem *csr_reg;
+ struct resource res;
+ struct clk *clk;
+ u64 denom;
+ u32 flags = 0;
+ int rc;
+
+ /* Check if the entry is disabled */
+ if (!of_device_is_available(np))
+ return;
+
+ /* Parse the DTS register for resource */
+ rc = of_address_to_resource(np, 0, &res);
+ if (rc != 0) {
+ pr_err("no DTS register for %s\n", np->full_name);
+ return;
+ }
+ csr_reg = of_iomap(np, 0);
+ if (!csr_reg) {
+ pr_err("Unable to map resource for %s\n", np->full_name);
+ return;
+ }
+ of_property_read_string(np, "clock-output-names", &clk_name);
+
+ denom = BIT(XGENE_CLK_PMD_WIDTH);
+ flags |= XGENE_CLK_PMD_SCALE_INVERTED;
+
+ clk = xgene_register_clk_pmd(NULL, clk_name,
+ of_clk_get_parent_name(np, 0), 0,
+ csr_reg, XGENE_CLK_PMD_SHIFT,
+ XGENE_CLK_PMD_WIDTH, denom,
+ flags, &clk_lock);
+ if (!IS_ERR(clk)) {
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+ pr_debug("Add %s clock\n", clk_name);
+ } else {
+ if (csr_reg)
+ iounmap(csr_reg);
+ }
+}
+
/* IP Clock */
struct xgene_dev_parameters {
void __iomem *csr_reg; /* CSR for IP clock */
@@ -543,6 +763,7 @@ err:
CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init);
CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init);
+CLK_OF_DECLARE(xgene_pmd_clock, "apm,xgene-pmd-clock", xgene_pmdclk_init);
CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock",
xgene_socpllclk_init);
CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock",
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 820a939..0fb39fe 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1908,10 +1908,6 @@ int clk_set_phase(struct clk *clk, int degrees)
clk_prepare_lock();
- /* bail early if nothing to do */
- if (degrees == clk->core->phase)
- goto out;
-
trace_clk_set_phase(clk->core, degrees);
if (clk->core->ops->set_phase)
@@ -1922,7 +1918,6 @@ int clk_set_phase(struct clk *clk, int degrees)
if (!ret)
clk->core->phase = degrees;
-out:
clk_prepare_unlock();
return ret;
@@ -2449,8 +2444,16 @@ static int __clk_core_init(struct clk_core *core)
hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
struct clk_core *parent = __clk_init_parent(orphan);
- if (parent)
- clk_core_reparent(orphan, parent);
+ /*
+ * we could call __clk_set_parent, but that would result in a
+ * redundant call to the .set_rate op, if it exists
+ */
+ if (parent) {
+ __clk_set_parent_before(orphan, parent);
+ __clk_set_parent_after(orphan, parent, NULL);
+ __clk_recalc_accuracies(orphan);
+ __clk_recalc_rates(orphan, 0);
+ }
}
/*
@@ -2491,7 +2494,7 @@ struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
/* This is to allow this function to be chained to others */
if (IS_ERR_OR_NULL(hw))
- return (struct clk *) hw;
+ return ERR_CAST(hw);
clk = kzalloc(sizeof(*clk), GFP_KERNEL);
if (!clk)
@@ -3166,19 +3169,14 @@ __of_clk_get_hw_from_provider(struct of_clk_provider *provider,
struct of_phandle_args *clkspec)
{
struct clk *clk;
- struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER);
- if (provider->get_hw) {
- hw = provider->get_hw(clkspec, provider->data);
- } else if (provider->get) {
- clk = provider->get(clkspec, provider->data);
- if (!IS_ERR(clk))
- hw = __clk_get_hw(clk);
- else
- hw = ERR_CAST(clk);
- }
+ if (provider->get_hw)
+ return provider->get_hw(clkspec, provider->data);
- return hw;
+ clk = provider->get(clkspec, provider->data);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+ return __clk_get_hw(clk);
}
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
@@ -3186,7 +3184,7 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
{
struct of_clk_provider *provider;
struct clk *clk = ERR_PTR(-EPROBE_DEFER);
- struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER);
+ struct clk_hw *hw;
if (!clkspec)
return ERR_PTR(-EINVAL);
@@ -3194,12 +3192,13 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
/* Check if we have such a provider in our array */
mutex_lock(&of_clk_mutex);
list_for_each_entry(provider, &of_clk_providers, link) {
- if (provider->node == clkspec->np)
+ if (provider->node == clkspec->np) {
hw = __of_clk_get_hw_from_provider(provider, clkspec);
- if (!IS_ERR(hw)) {
clk = __clk_create_clk(hw, dev_id, con_id);
+ }
- if (!IS_ERR(clk) && !__clk_get(clk)) {
+ if (!IS_ERR(clk)) {
+ if (!__clk_get(clk)) {
__clk_free_clk(clk);
clk = ERR_PTR(-ENOENT);
}
@@ -3451,6 +3450,10 @@ void __init of_clk_init(const struct of_device_id *matches)
&clk_provider_list, node) {
if (force || parent_ready(clk_provider->np)) {
+ /* Don't populate platform devices */
+ of_node_set_flag(clk_provider->np,
+ OF_POPULATED);
+
clk_provider->clk_init_cb(clk_provider->np);
of_clk_set_defaults(clk_provider->np, true);
diff --git a/drivers/clk/h8300/clk-div.c b/drivers/clk/h8300/clk-div.c
index 4bf44a2..715b882 100644
--- a/drivers/clk/h8300/clk-div.c
+++ b/drivers/clk/h8300/clk-div.c
@@ -14,7 +14,7 @@ static DEFINE_SPINLOCK(clklock);
static void __init h8300_div_clk_setup(struct device_node *node)
{
unsigned int num_parents;
- struct clk *clk;
+ struct clk_hw *hw;
const char *clk_name = node->name;
const char *parent_name;
void __iomem *divcr = NULL;
@@ -38,15 +38,15 @@ static void __init h8300_div_clk_setup(struct device_node *node)
parent_name = of_clk_get_parent_name(node, 0);
of_property_read_u32(node, "renesas,width", &width);
- clk = clk_register_divider(NULL, clk_name, parent_name,
+ hw = clk_hw_register_divider(NULL, clk_name, parent_name,
CLK_SET_RATE_GATE, divcr, offset, width,
CLK_DIVIDER_POWER_OF_TWO, &clklock);
- if (!IS_ERR(clk)) {
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ if (!IS_ERR(hw)) {
+ of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
return;
}
pr_err("%s: failed to register %s div clock (%ld)\n",
- __func__, clk_name, PTR_ERR(clk));
+ __func__, clk_name, PTR_ERR(hw));
error:
if (divcr)
iounmap(divcr);
diff --git a/drivers/clk/h8300/clk-h8s2678.c b/drivers/clk/h8300/clk-h8s2678.c
index c9c2fd5..a263124 100644
--- a/drivers/clk/h8300/clk-h8s2678.c
+++ b/drivers/clk/h8300/clk-h8s2678.c
@@ -84,11 +84,11 @@ static const struct clk_ops pll_ops = {
static void __init h8s2678_pll_clk_setup(struct device_node *node)
{
unsigned int num_parents;
- struct clk *clk;
const char *clk_name = node->name;
const char *parent_name;
struct pll_clock *pll_clock;
struct clk_init_data init;
+ int ret;
num_parents = of_clk_get_parent_count(node);
if (!num_parents) {
@@ -121,14 +121,14 @@ static void __init h8s2678_pll_clk_setup(struct device_node *node)
init.num_parents = 1;
pll_clock->hw.init = &init;
- clk = clk_register(NULL, &pll_clock->hw);
- if (IS_ERR(clk)) {
- pr_err("%s: failed to register %s div clock (%ld)\n",
- __func__, clk_name, PTR_ERR(clk));
+ ret = clk_hw_register(NULL, &pll_clock->hw);
+ if (ret) {
+ pr_err("%s: failed to register %s div clock (%d)\n",
+ __func__, clk_name, ret);
goto unmap_pllcr;
}
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll_clock->hw);
return;
unmap_pllcr:
diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c
index b0978d3..203cad6 100644
--- a/drivers/clk/imx/clk-imx35.c
+++ b/drivers/clk/imx/clk-imx35.c
@@ -66,20 +66,22 @@ static const char *std_sel[] = {"ppll", "arm"};
static const char *ipg_per_sel[] = {"ahb_per_div", "arm_per_div"};
enum mx35_clks {
- ckih, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, ipg,
- arm_per_div, ahb_per_div, ipg_per, uart_sel, uart_div, esdhc_sel,
- esdhc1_div, esdhc2_div, esdhc3_div, spdif_sel, spdif_div_pre,
- spdif_div_post, ssi_sel, ssi1_div_pre, ssi1_div_post, ssi2_div_pre,
- ssi2_div_post, usb_sel, usb_div, nfc_div, asrc_gate, pata_gate,
- audmux_gate, can1_gate, can2_gate, cspi1_gate, cspi2_gate, ect_gate,
- edio_gate, emi_gate, epit1_gate, epit2_gate, esai_gate, esdhc1_gate,
- esdhc2_gate, esdhc3_gate, fec_gate, gpio1_gate, gpio2_gate, gpio3_gate,
- gpt_gate, i2c1_gate, i2c2_gate, i2c3_gate, iomuxc_gate, ipu_gate,
- kpp_gate, mlb_gate, mshc_gate, owire_gate, pwm_gate, rngc_gate,
- rtc_gate, rtic_gate, scc_gate, sdma_gate, spba_gate, spdif_gate,
- ssi1_gate, ssi2_gate, uart1_gate, uart2_gate, uart3_gate, usbotg_gate,
- wdog_gate, max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate,
- gpu2d_gate, ckil, clk_max
+ /* 0 */ ckih, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb,
+ /* 9 */ ipg, arm_per_div, ahb_per_div, ipg_per, uart_sel, uart_div,
+ /* 15 */ esdhc_sel, esdhc1_div, esdhc2_div, esdhc3_div, spdif_sel,
+ /* 20 */ spdif_div_pre, spdif_div_post, ssi_sel, ssi1_div_pre,
+ /* 24 */ ssi1_div_post, ssi2_div_pre, ssi2_div_post, usb_sel, usb_div,
+ /* 29 */ nfc_div, asrc_gate, pata_gate, audmux_gate, can1_gate,
+ /* 34 */ can2_gate, cspi1_gate, cspi2_gate, ect_gate, edio_gate,
+ /* 39 */ emi_gate, epit1_gate, epit2_gate, esai_gate, esdhc1_gate,
+ /* 44 */ esdhc2_gate, esdhc3_gate, fec_gate, gpio1_gate, gpio2_gate,
+ /* 49 */ gpio3_gate, gpt_gate, i2c1_gate, i2c2_gate, i2c3_gate,
+ /* 54 */ iomuxc_gate, ipu_gate, kpp_gate, mlb_gate, mshc_gate,
+ /* 59 */ owire_gate, pwm_gate, rngc_gate, rtc_gate, rtic_gate, scc_gate,
+ /* 65 */ sdma_gate, spba_gate, spdif_gate, ssi1_gate, ssi2_gate,
+ /* 70 */ uart1_gate, uart2_gate, uart3_gate, usbotg_gate, wdog_gate,
+ /* 75 */ max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate,
+ /* 81 */ gpu2d_gate, ckil, clk_max
};
static struct clk *clk[clk_max];
@@ -115,7 +117,7 @@ static void __init _mx35_clocks_init(void)
}
clk[ckih] = imx_clk_fixed("ckih", 24000000);
- clk[ckil] = imx_clk_fixed("ckih", 32768);
+ clk[ckil] = imx_clk_fixed("ckil", 32768);
clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "mpll", "ckih", base + MX35_CCM_MPCTL);
clk[ppll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "ppll", "ckih", base + MX35_CCM_PPCTL);
diff --git a/drivers/clk/imx/clk-imx51-imx53.c b/drivers/clk/imx/clk-imx51-imx53.c
index 29d4c44..1e3c9ea 100644
--- a/drivers/clk/imx/clk-imx51-imx53.c
+++ b/drivers/clk/imx/clk-imx51-imx53.c
@@ -126,6 +126,7 @@ static const char *spdif0_com_sel[] = { "spdif0_podf", "ssi1_root_gate", };
static const char *mx51_spdif1_com_sel[] = { "spdif1_podf", "ssi2_root_gate", };
static const char *step_sels[] = { "lp_apm", };
static const char *cpu_podf_sels[] = { "pll1_sw", "step_sel" };
+static const char *ieee1588_sels[] = { "pll3_sw", "pll4_sw", "dummy" /* usbphy2_clk */, "dummy" /* fec_phy_clk */ };
static struct clk *clk[IMX5_CLK_END];
static struct clk_onecell_data clk_data;
@@ -543,6 +544,25 @@ static void __init mx53_clocks_init(struct device_node *np)
clk[IMX5_CLK_I2C3_GATE] = imx_clk_gate2("i2c3_gate", "per_root", MXC_CCM_CCGR1, 22);
clk[IMX5_CLK_SATA_GATE] = imx_clk_gate2("sata_gate", "ipg", MXC_CCM_CCGR4, 2);
+ clk[IMX5_CLK_FIRI_SEL] = imx_clk_mux("firi_sel", MXC_CCM_CSCMR2, 12, 2,
+ standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
+ clk[IMX5_CLK_FIRI_PRED] = imx_clk_divider("firi_pred", "firi_sel", MXC_CCM_CSCDR3, 6, 3);
+ clk[IMX5_CLK_FIRI_PODF] = imx_clk_divider("firi_podf", "firi_pred", MXC_CCM_CSCDR3, 0, 6);
+ clk[IMX5_CLK_FIRI_SERIAL_GATE] = imx_clk_gate2("firi_serial_gate", "firi_podf", MXC_CCM_CCGR1, 28);
+ clk[IMX5_CLK_FIRI_IPG_GATE] = imx_clk_gate2("firi_ipg_gate", "ipg", MXC_CCM_CCGR1, 26);
+
+ clk[IMX5_CLK_CSI0_MCLK1_SEL] = imx_clk_mux("csi0_mclk1_sel", MXC_CCM_CSCMR2, 22, 2,
+ standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
+ clk[IMX5_CLK_CSI0_MCLK1_PRED] = imx_clk_divider("csi0_mclk1_pred", "csi0_mclk1_sel", MXC_CCM_CSCDR4, 6, 3);
+ clk[IMX5_CLK_CSI0_MCLK1_PODF] = imx_clk_divider("csi0_mclk1_podf", "csi0_mclk1_pred", MXC_CCM_CSCDR4, 0, 6);
+ clk[IMX5_CLK_CSI0_MCLK1_GATE] = imx_clk_gate2("csi0_mclk1_serial_gate", "csi0_mclk1_podf", MXC_CCM_CCGR6, 4);
+
+ clk[IMX5_CLK_IEEE1588_SEL] = imx_clk_mux("ieee1588_sel", MXC_CCM_CSCMR2, 14, 2,
+ ieee1588_sels, ARRAY_SIZE(ieee1588_sels));
+ clk[IMX5_CLK_IEEE1588_PRED] = imx_clk_divider("ieee1588_pred", "ieee1588_sel", MXC_CCM_CSCDR2, 6, 3);
+ clk[IMX5_CLK_IEEE1588_PODF] = imx_clk_divider("ieee1588_podf", "ieee1588_pred", MXC_CCM_CSCDR2, 0, 6);
+ clk[IMX5_CLK_IEEE1588_GATE] = imx_clk_gate2("ieee1588_serial_gate", "ieee1588_podf", MXC_CCM_CCGR7, 6);
+
clk[IMX5_CLK_CKO1_SEL] = imx_clk_mux("cko1_sel", MXC_CCM_CCOSR, 0, 4,
mx53_cko1_sel, ARRAY_SIZE(mx53_cko1_sel));
clk[IMX5_CLK_CKO1_PODF] = imx_clk_divider("cko1_podf", "cko1_sel", MXC_CCM_CCOSR, 4, 3);
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
index ba1c1ae..ce8ea10 100644
--- a/drivers/clk/imx/clk-imx6q.c
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -318,11 +318,16 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_IPG_PER_SEL] = imx_clk_mux("ipg_per_sel", base + 0x1c, 6, 1, ipg_per_sels, ARRAY_SIZE(ipg_per_sels));
clk[IMX6QDL_CLK_UART_SEL] = imx_clk_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels));
clk[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels_2, ARRAY_SIZE(gpu2d_core_sels_2));
+ } else if (clk_on_imx6dl()) {
+ clk[IMX6QDL_CLK_MLB_SEL] = imx_clk_mux("mlb_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels));
} else {
clk[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels));
}
clk[IMX6QDL_CLK_GPU3D_CORE_SEL] = imx_clk_mux("gpu3d_core_sel", base + 0x18, 4, 2, gpu3d_core_sels, ARRAY_SIZE(gpu3d_core_sels));
- clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels));
+ if (clk_on_imx6dl())
+ clk[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_mux("gpu2d_core_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels));
+ else
+ clk[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels));
clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels));
clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT);
@@ -400,9 +405,15 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_LDB_DI0_DIV_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
clk[IMX6QDL_CLK_LDB_DI1_DIV_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
}
- clk[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 23, 3);
+ if (clk_on_imx6dl())
+ clk[IMX6QDL_CLK_MLB_PODF] = imx_clk_divider("mlb_podf", "mlb_sel", base + 0x18, 23, 3);
+ else
+ clk[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 23, 3);
clk[IMX6QDL_CLK_GPU3D_CORE_PODF] = imx_clk_divider("gpu3d_core_podf", "gpu3d_core_sel", base + 0x18, 26, 3);
- clk[IMX6QDL_CLK_GPU3D_SHADER] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3);
+ if (clk_on_imx6dl())
+ clk[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 29, 3);
+ else
+ clk[IMX6QDL_CLK_GPU3D_SHADER] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3);
clk[IMX6QDL_CLK_IPU1_PODF] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3);
clk[IMX6QDL_CLK_IPU2_PODF] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3);
clk[IMX6QDL_CLK_LDB_DI0_PODF] = imx_clk_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0);
@@ -473,14 +484,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x6c, 16, &share_count_esai);
clk[IMX6QDL_CLK_GPT_IPG] = imx_clk_gate2("gpt_ipg", "ipg", base + 0x6c, 20);
clk[IMX6QDL_CLK_GPT_IPG_PER] = imx_clk_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22);
- if (clk_on_imx6dl())
- /*
- * The multiplexer and divider of imx6q clock gpu3d_shader get
- * redefined/reused as gpu2d_core_sel and gpu2d_core_podf on imx6dl.
- */
- clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu3d_shader", base + 0x6c, 24);
- else
- clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24);
+ clk[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24);
clk[IMX6QDL_CLK_GPU3D_CORE] = imx_clk_gate2("gpu3d_core", "gpu3d_core_podf", base + 0x6c, 26);
clk[IMX6QDL_CLK_HDMI_IAHB] = imx_clk_gate2("hdmi_iahb", "ahb", base + 0x70, 0);
clk[IMX6QDL_CLK_HDMI_ISFR] = imx_clk_gate2("hdmi_isfr", "video_27m", base + 0x70, 4);
@@ -511,7 +515,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
* The multiplexer and divider of the imx6q clock gpu2d get
* redefined/reused as mlb_sys_sel and mlb_sys_clk_podf on imx6dl.
*/
- clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "gpu2d_core_podf", base + 0x74, 18);
+ clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "mlb_podf", base + 0x74, 18);
else
clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "axi", base + 0x74, 18);
clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20);
@@ -629,6 +633,24 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
if (IS_ENABLED(CONFIG_PCI_IMX6))
clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]);
+ /*
+ * Initialize the GPU clock muxes, so that the maximum specified clock
+ * rates for the respective SoC are not exceeded.
+ */
+ if (clk_on_imx6dl()) {
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ } else if (clk_on_imx6q()) {
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL],
+ clk[IMX6QDL_CLK_MMDC_CH0_AXI]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU3D_SHADER_SEL],
+ clk[IMX6QDL_CLK_PLL2_PFD1_594M]);
+ clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL],
+ clk[IMX6QDL_CLK_PLL3_USB_OTG]);
+ }
+
imx_register_uart_clocks(uart_clks);
}
CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init);
diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c
index 6ed4f8f..e7c7353 100644
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -22,43 +22,63 @@
#include "clk.h"
+static u32 share_count_sai1;
+static u32 share_count_sai2;
+static u32 share_count_sai3;
+
+static struct clk_div_table test_div_table[] = {
+ { .val = 3, .div = 1, },
+ { .val = 2, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 0, .div = 4, },
+ { }
+};
+
+static struct clk_div_table post_div_table[] = {
+ { .val = 3, .div = 4, },
+ { .val = 2, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 0, .div = 1, },
+ { }
+};
+
static struct clk *clks[IMX7D_CLK_END];
static const char *arm_a7_sel[] = { "osc", "pll_arm_main_clk",
"pll_enet_500m_clk", "pll_dram_main_clk",
- "pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_audio_main_clk",
+ "pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_audio_post_div",
"pll_usb_main_clk", };
static const char *arm_m4_sel[] = { "osc", "pll_sys_main_240m_clk",
"pll_enet_250m_clk", "pll_sys_pfd2_270m_clk",
- "pll_dram_533m_clk", "pll_audio_main_clk", "pll_video_main_clk",
+ "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk",
"pll_usb_main_clk", };
static const char *arm_m0_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_125m_clk", "pll_sys_pfd2_135m_clk",
- "pll_dram_533m_clk", "pll_audio_main_clk", "pll_video_main_clk",
+ "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk",
"pll_usb_main_clk", };
static const char *axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk",
"pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd5_clk",
- "pll_audio_main_clk", "pll_video_main_clk", "pll_sys_pfd7_clk", };
+ "pll_audio_post_div", "pll_video_main_clk", "pll_sys_pfd7_clk", };
static const char *disp_axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk",
"pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd6_clk",
- "pll_sys_pfd7_clk", "pll_audio_main_clk", "pll_video_main_clk", };
+ "pll_sys_pfd7_clk", "pll_audio_post_div", "pll_video_main_clk", };
static const char *enet_axi_sel[] = { "osc", "pll_sys_pfd2_270m_clk",
"pll_dram_533m_clk", "pll_enet_250m_clk",
- "pll_sys_main_240m_clk", "pll_audio_main_clk", "pll_video_main_clk",
+ "pll_sys_main_240m_clk", "pll_audio_post_div", "pll_video_main_clk",
"pll_sys_pfd4_clk", };
static const char *nand_usdhc_bus_sel[] = { "osc", "pll_sys_pfd2_270m_clk",
"pll_dram_533m_clk", "pll_sys_main_240m_clk",
"pll_sys_pfd2_135m_clk", "pll_sys_pfd6_clk", "pll_enet_250m_clk",
- "pll_audio_main_clk", };
+ "pll_audio_post_div", };
static const char *ahb_channel_sel[] = { "osc", "pll_sys_pfd2_270m_clk",
"pll_dram_533m_clk", "pll_sys_pfd0_392m_clk",
- "pll_enet_125m_clk", "pll_usb_main_clk", "pll_audio_main_clk",
+ "pll_enet_125m_clk", "pll_usb_main_clk", "pll_audio_post_div",
"pll_video_main_clk", };
static const char *dram_phym_sel[] = { "pll_dram_main_clk",
@@ -69,13 +89,13 @@ static const char *dram_sel[] = { "pll_dram_main_clk",
static const char *dram_phym_alt_sel[] = { "osc", "pll_dram_533m_clk",
"pll_sys_main_clk", "pll_enet_500m_clk",
- "pll_usb_main_clk", "pll_sys_pfd7_clk", "pll_audio_main_clk",
+ "pll_usb_main_clk", "pll_sys_pfd7_clk", "pll_audio_post_div",
"pll_video_main_clk", };
static const char *dram_alt_sel[] = { "osc", "pll_dram_533m_clk",
"pll_sys_main_clk", "pll_enet_500m_clk",
"pll_enet_250m_clk", "pll_sys_pfd0_392m_clk",
- "pll_audio_main_clk", "pll_sys_pfd2_270m_clk", };
+ "pll_audio_post_div", "pll_sys_pfd2_270m_clk", };
static const char *usb_hsic_sel[] = { "osc", "pll_sys_main_clk",
"pll_usb_main_clk", "pll_sys_pfd3_clk", "pll_sys_pfd4_clk",
@@ -101,53 +121,53 @@ static const char *lcdif_pixel_sel[] = { "osc", "pll_sys_pfd5_clk",
static const char *mipi_dsi_sel[] = { "osc", "pll_sys_pfd5_clk",
"pll_sys_pfd3_clk", "pll_sys_main_clk", "pll_sys_pfd0_196m_clk",
- "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_main_clk", };
+ "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_post_div", };
static const char *mipi_csi_sel[] = { "osc", "pll_sys_pfd4_clk",
"pll_sys_pfd3_clk", "pll_sys_main_clk", "pll_sys_pfd0_196m_clk",
- "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_main_clk", };
+ "pll_dram_533m_clk", "pll_video_main_clk", "pll_audio_post_div", };
static const char *mipi_dphy_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_dram_533m_clk", "pll_sys_pfd5_clk", "ref_1m_clk", "ext_clk_2",
"pll_video_main_clk", "ext_clk_3", };
static const char *sai1_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
- "pll_audio_main_clk", "pll_dram_533m_clk", "pll_video_main_clk",
+ "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk",
"pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_2", };
static const char *sai2_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
- "pll_audio_main_clk", "pll_dram_533m_clk", "pll_video_main_clk",
+ "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk",
"pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_2", };
static const char *sai3_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
- "pll_audio_main_clk", "pll_dram_533m_clk", "pll_video_main_clk",
+ "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk",
"pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_3", };
static const char *spdif_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
- "pll_audio_main_clk", "pll_dram_533m_clk", "pll_video_main_clk",
+ "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_main_clk",
"pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_3_clk", };
static const char *enet1_ref_sel[] = { "osc", "pll_enet_125m_clk",
"pll_enet_50m_clk", "pll_enet_25m_clk",
- "pll_sys_main_120m_clk", "pll_audio_main_clk", "pll_video_main_clk",
+ "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_main_clk",
"ext_clk_4", };
static const char *enet1_time_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_audio_main_clk", "ext_clk_1", "ext_clk_2", "ext_clk_3",
+ "pll_audio_post_div", "ext_clk_1", "ext_clk_2", "ext_clk_3",
"ext_clk_4", "pll_video_main_clk", };
static const char *enet2_ref_sel[] = { "osc", "pll_enet_125m_clk",
"pll_enet_50m_clk", "pll_enet_25m_clk",
- "pll_sys_main_120m_clk", "pll_audio_main_clk", "pll_video_main_clk",
+ "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_main_clk",
"ext_clk_4", };
static const char *enet2_time_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_audio_main_clk", "ext_clk_1", "ext_clk_2", "ext_clk_3",
+ "pll_audio_post_div", "ext_clk_1", "ext_clk_2", "ext_clk_3",
"ext_clk_4", "pll_video_main_clk", };
static const char *enet_phy_ref_sel[] = { "osc", "pll_enet_25m_clk",
"pll_enet_50m_clk", "pll_enet_125m_clk",
- "pll_dram_533m_clk", "pll_audio_main_clk", "pll_video_main_clk",
+ "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk",
"pll_sys_pfd3_clk", };
static const char *eim_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
@@ -188,22 +208,22 @@ static const char *can2_sel[] = { "osc", "pll_sys_main_120m_clk",
static const char *i2c1_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_50m_clk", "pll_dram_533m_clk",
- "pll_audio_main_clk", "pll_video_main_clk", "pll_usb_main_clk",
+ "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk",
"pll_sys_pfd2_135m_clk", };
static const char *i2c2_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_50m_clk", "pll_dram_533m_clk",
- "pll_audio_main_clk", "pll_video_main_clk", "pll_usb_main_clk",
+ "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk",
"pll_sys_pfd2_135m_clk", };
static const char *i2c3_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_50m_clk", "pll_dram_533m_clk",
- "pll_audio_main_clk", "pll_video_main_clk", "pll_usb_main_clk",
+ "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk",
"pll_sys_pfd2_135m_clk", };
static const char *i2c4_sel[] = { "osc", "pll_sys_main_120m_clk",
"pll_enet_50m_clk", "pll_dram_533m_clk",
- "pll_audio_main_clk", "pll_video_main_clk", "pll_usb_main_clk",
+ "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk",
"pll_sys_pfd2_135m_clk", };
static const char *uart1_sel[] = { "osc", "pll_sys_main_240m_clk",
@@ -262,32 +282,32 @@ static const char *ecspi4_sel[] = { "osc", "pll_sys_main_240m_clk",
"pll_usb_main_clk", };
static const char *pwm1_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk",
+ "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
"ext_clk_1", "ref_1m_clk", "pll_video_main_clk", };
static const char *pwm2_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk",
+ "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
"ext_clk_1", "ref_1m_clk", "pll_video_main_clk", };
static const char *pwm3_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk",
+ "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
"ext_clk_2", "ref_1m_clk", "pll_video_main_clk", };
static const char *pwm4_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk",
+ "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
"ext_clk_2", "ref_1m_clk", "pll_video_main_clk", };
static const char *flextimer1_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk",
+ "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
"ext_clk_3", "ref_1m_clk", "pll_video_main_clk", };
static const char *flextimer2_sel[] = { "osc", "pll_enet_100m_clk",
- "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_main_clk",
+ "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div",
"ext_clk_3", "ref_1m_clk", "pll_video_main_clk", };
static const char *sim1_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
"pll_sys_main_120m_clk", "pll_dram_533m_clk",
- "pll_usb_main_clk", "pll_audio_main_clk", "pll_enet_125m_clk",
+ "pll_usb_main_clk", "pll_audio_post_div", "pll_enet_125m_clk",
"pll_sys_pfd7_clk", };
static const char *sim2_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
@@ -297,19 +317,19 @@ static const char *sim2_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
static const char *gpt1_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk",
- "ref_1m_clk", "pll_audio_main_clk", "ext_clk_1", };
+ "ref_1m_clk", "pll_audio_post_div", "ext_clk_1", };
static const char *gpt2_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk",
- "ref_1m_clk", "pll_audio_main_clk", "ext_clk_2", };
+ "ref_1m_clk", "pll_audio_post_div", "ext_clk_2", };
static const char *gpt3_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk",
- "ref_1m_clk", "pll_audio_main_clk", "ext_clk_3", };
+ "ref_1m_clk", "pll_audio_post_div", "ext_clk_3", };
static const char *gpt4_sel[] = { "osc", "pll_enet_100m_clk",
"pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_main_clk",
- "ref_1m_clk", "pll_audio_main_clk", "ext_clk_4", };
+ "ref_1m_clk", "pll_audio_post_div", "ext_clk_4", };
static const char *trace_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
"pll_sys_main_120m_clk", "pll_dram_533m_clk",
@@ -323,12 +343,12 @@ static const char *wdog_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
static const char *csi_mclk_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
"pll_sys_main_120m_clk", "pll_dram_533m_clk",
- "pll_enet_125m_clk", "pll_audio_main_clk", "pll_video_main_clk",
+ "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_main_clk",
"pll_usb_main_clk", };
static const char *audio_mclk_sel[] = { "osc", "pll_sys_pfd2_135m_clk",
"pll_sys_main_120m_clk", "pll_dram_533m_clk",
- "pll_enet_125m_clk", "pll_audio_main_clk", "pll_video_main_clk",
+ "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_main_clk",
"pll_usb_main_clk", };
static const char *wrclk_sel[] = { "osc", "pll_enet_40m_clk",
@@ -342,13 +362,13 @@ static const char *clko1_sel[] = { "osc", "pll_sys_main_clk",
static const char *clko2_sel[] = { "osc", "pll_sys_main_240m_clk",
"pll_sys_pfd0_392m_clk", "pll_sys_pfd1_166m_clk", "pll_sys_pfd4_clk",
- "pll_audio_main_clk", "pll_video_main_clk", "ckil", };
+ "pll_audio_post_div", "pll_video_main_clk", "ckil", };
static const char *lvds1_sel[] = { "pll_arm_main_clk",
"pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd1_332m_clk",
"pll_sys_pfd2_270m_clk", "pll_sys_pfd3_clk", "pll_sys_pfd4_clk",
"pll_sys_pfd5_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk",
- "pll_audio_main_clk", "pll_video_main_clk", "pll_enet_500m_clk",
+ "pll_audio_post_div", "pll_video_main_clk", "pll_enet_500m_clk",
"pll_enet_250m_clk", "pll_enet_125m_clk", "pll_enet_100m_clk",
"pll_enet_50m_clk", "pll_enet_40m_clk", "pll_enet_25m_clk",
"pll_dram_main_clk", };
@@ -430,6 +450,11 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_PLL_AUDIO_MAIN_CLK] = imx_clk_gate("pll_audio_main_clk", "pll_audio_main_bypass", base + 0xf0, 13);
clks[IMX7D_PLL_VIDEO_MAIN_CLK] = imx_clk_gate("pll_video_main_clk", "pll_video_main_bypass", base + 0x130, 13);
+ clks[IMX7D_PLL_AUDIO_TEST_DIV] = clk_register_divider_table(NULL, "pll_audio_test_div", "pll_audio_main_clk",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xf0, 19, 2, 0, test_div_table, &imx_ccm_lock);
+ clks[IMX7D_PLL_AUDIO_POST_DIV] = clk_register_divider_table(NULL, "pll_audio_post_div", "pll_audio_test_div",
+ CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xf0, 22, 2, 0, post_div_table, &imx_ccm_lock);
+
clks[IMX7D_PLL_SYS_PFD0_392M_CLK] = imx_clk_pfd("pll_sys_pfd0_392m_clk", "pll_sys_main_clk", base + 0xc0, 0);
clks[IMX7D_PLL_SYS_PFD1_332M_CLK] = imx_clk_pfd("pll_sys_pfd1_332m_clk", "pll_sys_main_clk", base + 0xc0, 1);
clks[IMX7D_PLL_SYS_PFD2_270M_CLK] = imx_clk_pfd("pll_sys_pfd2_270m_clk", "pll_sys_main_clk", base + 0xc0, 2);
@@ -779,6 +804,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_DRAM_PHYM_ALT_ROOT_CLK] = imx_clk_gate4("dram_phym_alt_root_clk", "dram_phym_alt_post_div", base + 0x4130, 0);
clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0);
clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4420, 0);
+ clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0);
clks[IMX7D_PCIE_CTRL_ROOT_CLK] = imx_clk_gate4("pcie_ctrl_root_clk", "pcie_ctrl_post_div", base + 0x4600, 0);
clks[IMX7D_PCIE_PHY_ROOT_CLK] = imx_clk_gate4("pcie_phy_root_clk", "pcie_phy_post_div", base + 0x4600, 0);
clks[IMX7D_EPDC_PIXEL_ROOT_CLK] = imx_clk_gate4("epdc_pixel_root_clk", "epdc_pixel_post_div", base + 0x44a0, 0);
@@ -786,9 +812,12 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_MIPI_DSI_ROOT_CLK] = imx_clk_gate4("mipi_dsi_root_clk", "mipi_dsi_post_div", base + 0x4650, 0);
clks[IMX7D_MIPI_CSI_ROOT_CLK] = imx_clk_gate4("mipi_csi_root_clk", "mipi_csi_post_div", base + 0x4640, 0);
clks[IMX7D_MIPI_DPHY_ROOT_CLK] = imx_clk_gate4("mipi_dphy_root_clk", "mipi_dphy_post_div", base + 0x4660, 0);
- clks[IMX7D_SAI1_ROOT_CLK] = imx_clk_gate4("sai1_root_clk", "sai1_post_div", base + 0x48c0, 0);
- clks[IMX7D_SAI2_ROOT_CLK] = imx_clk_gate4("sai2_root_clk", "sai2_post_div", base + 0x48d0, 0);
- clks[IMX7D_SAI3_ROOT_CLK] = imx_clk_gate4("sai3_root_clk", "sai3_post_div", base + 0x48e0, 0);
+ clks[IMX7D_SAI1_ROOT_CLK] = imx_clk_gate2_shared2("sai1_root_clk", "sai1_post_div", base + 0x48c0, 0, &share_count_sai1);
+ clks[IMX7D_SAI1_IPG_CLK] = imx_clk_gate2_shared2("sai1_ipg_clk", "ipg_root_clk", base + 0x48c0, 0, &share_count_sai1);
+ clks[IMX7D_SAI2_ROOT_CLK] = imx_clk_gate2_shared2("sai2_root_clk", "sai2_post_div", base + 0x48d0, 0, &share_count_sai2);
+ clks[IMX7D_SAI2_IPG_CLK] = imx_clk_gate2_shared2("sai2_ipg_clk", "ipg_root_clk", base + 0x48d0, 0, &share_count_sai2);
+ clks[IMX7D_SAI3_ROOT_CLK] = imx_clk_gate2_shared2("sai3_root_clk", "sai3_post_div", base + 0x48e0, 0, &share_count_sai3);
+ clks[IMX7D_SAI3_IPG_CLK] = imx_clk_gate2_shared2("sai3_ipg_clk", "ipg_root_clk", base + 0x48e0, 0, &share_count_sai3);
clks[IMX7D_SPDIF_ROOT_CLK] = imx_clk_gate4("spdif_root_clk", "spdif_post_div", base + 0x44d0, 0);
clks[IMX7D_ENET1_REF_ROOT_CLK] = imx_clk_gate4("enet1_ref_root_clk", "enet1_ref_post_div", base + 0x44e0, 0);
clks[IMX7D_ENET1_TIME_ROOT_CLK] = imx_clk_gate4("enet1_time_root_clk", "enet1_time_post_div", base + 0x44f0, 0);
@@ -860,8 +889,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
/* use old gpt clk setting, gpt1 root clk must be twice as gpt counter freq */
clk_set_parent(clks[IMX7D_GPT1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]);
- clk_set_parent(clks[IMX7D_ENET_AXI_ROOT_SRC], clks[IMX7D_PLL_ENET_MAIN_250M_CLK]);
-
/* set uart module clock's parent clock source that must be great then 80MHz */
clk_set_parent(clks[IMX7D_UART1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]);
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index a81c038..3799ff8 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -134,6 +134,15 @@ static inline struct clk *imx_clk_gate2_shared(const char *name,
shift, 0x3, 0, &imx_ccm_lock, share_count);
}
+static inline struct clk *imx_clk_gate2_shared2(const char *name,
+ const char *parent, void __iomem *reg, u8 shift,
+ unsigned int *share_count)
+{
+ return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT |
+ CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0,
+ &imx_ccm_lock, share_count);
+}
+
static inline struct clk *imx_clk_gate2_cgr(const char *name,
const char *parent, void __iomem *reg, u8 shift, u8 cgr_val)
{
diff --git a/drivers/clk/loongson1/Makefile b/drivers/clk/loongson1/Makefile
new file mode 100644
index 0000000..b7f6a16
--- /dev/null
+++ b/drivers/clk/loongson1/Makefile
@@ -0,0 +1,3 @@
+obj-y += clk.o
+obj-$(CONFIG_LOONGSON1_LS1B) += clk-loongson1b.o
+obj-$(CONFIG_LOONGSON1_LS1C) += clk-loongson1c.o
diff --git a/drivers/clk/loongson1/clk-loongson1b.c b/drivers/clk/loongson1/clk-loongson1b.c
new file mode 100644
index 0000000..f36a97e
--- /dev/null
+++ b/drivers/clk/loongson1/clk-loongson1b.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include <loongson1.h>
+#include "clk.h"
+
+#define OSC (33 * 1000000)
+#define DIV_APB 2
+
+static DEFINE_SPINLOCK(_lock);
+
+static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u32 pll, rate;
+
+ pll = __raw_readl(LS1X_CLK_PLL_FREQ);
+ rate = 12 + (pll & GENMASK(5, 0));
+ rate *= OSC;
+ rate >>= 1;
+
+ return rate;
+}
+
+static const struct clk_ops ls1x_pll_clk_ops = {
+ .recalc_rate = ls1x_pll_recalc_rate,
+};
+
+static const char *const cpu_parents[] = { "cpu_clk_div", "osc_clk", };
+static const char *const ahb_parents[] = { "ahb_clk_div", "osc_clk", };
+static const char *const dc_parents[] = { "dc_clk_div", "osc_clk", };
+
+void __init ls1x_clk_init(void)
+{
+ struct clk_hw *hw;
+
+ hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC);
+ clk_hw_register_clkdev(hw, "osc_clk", NULL);
+
+ /* clock derived from 33 MHz OSC clk */
+ hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk",
+ &ls1x_pll_clk_ops, 0);
+ clk_hw_register_clkdev(hw, "pll_clk", NULL);
+
+ /* clock derived from PLL clk */
+ /* _____
+ * _______________________| |
+ * OSC ___/ | MUX |___ CPU CLK
+ * \___ PLL ___ CPU DIV ___| |
+ * |_____|
+ */
+ hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk",
+ CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV,
+ DIV_CPU_SHIFT, DIV_CPU_WIDTH,
+ CLK_DIVIDER_ONE_BASED |
+ CLK_DIVIDER_ROUND_CLOSEST, &_lock);
+ clk_hw_register_clkdev(hw, "cpu_clk_div", NULL);
+ hw = clk_hw_register_mux(NULL, "cpu_clk", cpu_parents,
+ ARRAY_SIZE(cpu_parents),
+ CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
+ BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock);
+ clk_hw_register_clkdev(hw, "cpu_clk", NULL);
+
+ /* _____
+ * _______________________| |
+ * OSC ___/ | MUX |___ DC CLK
+ * \___ PLL ___ DC DIV ___| |
+ * |_____|
+ */
+ hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk",
+ 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
+ DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_hw_register_clkdev(hw, "dc_clk_div", NULL);
+ hw = clk_hw_register_mux(NULL, "dc_clk", dc_parents,
+ ARRAY_SIZE(dc_parents),
+ CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
+ BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock);
+ clk_hw_register_clkdev(hw, "dc_clk", NULL);
+
+ /* _____
+ * _______________________| |
+ * OSC ___/ | MUX |___ DDR CLK
+ * \___ PLL ___ DDR DIV ___| |
+ * |_____|
+ */
+ hw = clk_hw_register_divider(NULL, "ahb_clk_div", "pll_clk",
+ 0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
+ DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED,
+ &_lock);
+ clk_hw_register_clkdev(hw, "ahb_clk_div", NULL);
+ hw = clk_hw_register_mux(NULL, "ahb_clk", ahb_parents,
+ ARRAY_SIZE(ahb_parents),
+ CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
+ BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock);
+ clk_hw_register_clkdev(hw, "ahb_clk", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-dma", NULL);
+ clk_hw_register_clkdev(hw, "stmmaceth", NULL);
+
+ /* clock derived from AHB clk */
+ /* APB clk is always half of the AHB clk */
+ hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
+ DIV_APB);
+ clk_hw_register_clkdev(hw, "apb_clk", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-ac97", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-i2c", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-nand", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-spi", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-wdt", NULL);
+ clk_hw_register_clkdev(hw, "serial8250", NULL);
+}
diff --git a/drivers/clk/loongson1/clk-loongson1c.c b/drivers/clk/loongson1/clk-loongson1c.c
new file mode 100644
index 0000000..3466f73
--- /dev/null
+++ b/drivers/clk/loongson1/clk-loongson1c.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+#include <loongson1.h>
+#include "clk.h"
+
+#define OSC (24 * 1000000)
+#define DIV_APB 1
+
+static DEFINE_SPINLOCK(_lock);
+
+static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u32 pll, rate;
+
+ pll = __raw_readl(LS1X_CLK_PLL_FREQ);
+ rate = ((pll >> 8) & 0xff) + ((pll >> 16) & 0xff);
+ rate *= OSC;
+ rate >>= 2;
+
+ return rate;
+}
+
+static const struct clk_ops ls1x_pll_clk_ops = {
+ .recalc_rate = ls1x_pll_recalc_rate,
+};
+
+static const struct clk_div_table ahb_div_table[] = {
+ [0] = { .val = 0, .div = 2 },
+ [1] = { .val = 1, .div = 4 },
+ [2] = { .val = 2, .div = 3 },
+ [3] = { .val = 3, .div = 3 },
+};
+
+void __init ls1x_clk_init(void)
+{
+ struct clk_hw *hw;
+
+ hw = clk_hw_register_fixed_rate(NULL, "osc_clk", NULL, 0, OSC);
+ clk_hw_register_clkdev(hw, "osc_clk", NULL);
+
+ /* clock derived from 24 MHz OSC clk */
+ hw = clk_hw_register_pll(NULL, "pll_clk", "osc_clk",
+ &ls1x_pll_clk_ops, 0);
+ clk_hw_register_clkdev(hw, "pll_clk", NULL);
+
+ hw = clk_hw_register_divider(NULL, "cpu_clk_div", "pll_clk",
+ CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV,
+ DIV_CPU_SHIFT, DIV_CPU_WIDTH,
+ CLK_DIVIDER_ONE_BASED |
+ CLK_DIVIDER_ROUND_CLOSEST, &_lock);
+ clk_hw_register_clkdev(hw, "cpu_clk_div", NULL);
+ hw = clk_hw_register_fixed_factor(NULL, "cpu_clk", "cpu_clk_div",
+ 0, 1, 1);
+ clk_hw_register_clkdev(hw, "cpu_clk", NULL);
+
+ hw = clk_hw_register_divider(NULL, "dc_clk_div", "pll_clk",
+ 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
+ DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_hw_register_clkdev(hw, "dc_clk_div", NULL);
+ hw = clk_hw_register_fixed_factor(NULL, "dc_clk", "dc_clk_div",
+ 0, 1, 1);
+ clk_hw_register_clkdev(hw, "dc_clk", NULL);
+
+ hw = clk_hw_register_divider_table(NULL, "ahb_clk_div", "cpu_clk_div",
+ 0, LS1X_CLK_PLL_FREQ, DIV_DDR_SHIFT,
+ DIV_DDR_WIDTH, CLK_DIVIDER_ALLOW_ZERO,
+ ahb_div_table, &_lock);
+ clk_hw_register_clkdev(hw, "ahb_clk_div", NULL);
+ hw = clk_hw_register_fixed_factor(NULL, "ahb_clk", "ahb_clk_div",
+ 0, 1, 1);
+ clk_hw_register_clkdev(hw, "ahb_clk", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-dma", NULL);
+ clk_hw_register_clkdev(hw, "stmmaceth", NULL);
+
+ /* clock derived from AHB clk */
+ hw = clk_hw_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
+ DIV_APB);
+ clk_hw_register_clkdev(hw, "apb_clk", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-ac97", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-i2c", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-nand", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-pwmtimer", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-spi", NULL);
+ clk_hw_register_clkdev(hw, "ls1x-wdt", NULL);
+ clk_hw_register_clkdev(hw, "serial8250", NULL);
+}
diff --git a/drivers/clk/loongson1/clk.c b/drivers/clk/loongson1/clk.c
new file mode 100644
index 0000000..cfcfd14
--- /dev/null
+++ b/drivers/clk/loongson1/clk.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+
+struct clk_hw *__init clk_hw_register_pll(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ const struct clk_ops *ops,
+ unsigned long flags)
+{
+ int ret;
+ struct clk_hw *hw;
+ struct clk_init_data init;
+
+ /* allocate the divider */
+ hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+ if (!hw)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ hw->init = &init;
+
+ /* register the clock */
+ ret = clk_hw_register(dev, hw);
+ if (ret) {
+ kfree(hw);
+ hw = ERR_PTR(ret);
+ }
+
+ return hw;
+}
diff --git a/drivers/clk/loongson1/clk.h b/drivers/clk/loongson1/clk.h
new file mode 100644
index 0000000..085d74b
--- /dev/null
+++ b/drivers/clk/loongson1/clk.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __LOONGSON1_CLK_H
+#define __LOONGSON1_CLK_H
+
+struct clk_hw *clk_hw_register_pll(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ const struct clk_ops *ops,
+ unsigned long flags);
+
+#endif /* __LOONGSON1_CLK_H */
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
new file mode 100644
index 0000000..380c372
--- /dev/null
+++ b/drivers/clk/mediatek/Kconfig
@@ -0,0 +1,21 @@
+#
+# MediaTek SoC drivers
+#
+config COMMON_CLK_MEDIATEK
+ bool
+ ---help---
+ Mediatek SoCs' clock support.
+
+config COMMON_CLK_MT8135
+ bool "Clock driver for Mediatek MT8135"
+ select COMMON_CLK_MEDIATEK
+ default ARCH_MEDIATEK
+ ---help---
+ This driver supports Mediatek MT8135 clocks.
+
+config COMMON_CLK_MT8173
+ bool "Clock driver for Mediatek MT8173"
+ select COMMON_CLK_MEDIATEK
+ default ARCH_MEDIATEK
+ ---help---
+ This driver supports Mediatek MT8173 clocks.
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 95fdfac..32e7222 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,4 +1,4 @@
-obj-y += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
+obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
obj-$(CONFIG_RESET_CONTROLLER) += reset.o
-obj-y += clk-mt8135.o
-obj-y += clk-mt8173.o
+obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
+obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
index 2a76901..d8787bf 100644
--- a/drivers/clk/mediatek/clk-gate.c
+++ b/drivers/clk/mediatek/clk-gate.c
@@ -97,7 +97,7 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
.disable = mtk_cg_disable_inv,
};
-struct clk * __init mtk_clk_register_gate(
+struct clk *mtk_clk_register_gate(
const char *name,
const char *parent_name,
struct regmap *regmap,
diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
index 10c9860..0ac3aee 100644
--- a/drivers/clk/mediatek/clk-mt8173.c
+++ b/drivers/clk/mediatek/clk-mt8173.c
@@ -1074,8 +1074,10 @@ static void __init mtk_apmixedsys_init(struct device_node *node)
}
mt8173_pll_clk_data = clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK);
- if (!clk_data)
+ if (!clk_data) {
+ iounmap(base);
return;
+ }
mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data);
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index 5ada644..bb30f70 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -24,7 +24,7 @@
#include "clk-mtk.h"
#include "clk-gate.h"
-struct clk_onecell_data * __init mtk_alloc_clk_data(unsigned int clk_num)
+struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num)
{
int i;
struct clk_onecell_data *clk_data;
@@ -49,7 +49,7 @@ err_out:
return NULL;
}
-void __init mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks,
+void mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks,
int num, struct clk_onecell_data *clk_data)
{
int i;
@@ -72,7 +72,7 @@ void __init mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks,
}
}
-void __init mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
+void mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
int num, struct clk_onecell_data *clk_data)
{
int i;
@@ -95,7 +95,7 @@ void __init mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
}
}
-int __init mtk_clk_register_gates(struct device_node *node,
+int mtk_clk_register_gates(struct device_node *node,
const struct mtk_gate *clks,
int num, struct clk_onecell_data *clk_data)
{
@@ -135,7 +135,7 @@ int __init mtk_clk_register_gates(struct device_node *node,
return 0;
}
-struct clk * __init mtk_clk_register_composite(const struct mtk_composite *mc,
+struct clk *mtk_clk_register_composite(const struct mtk_composite *mc,
void __iomem *base, spinlock_t *lock)
{
struct clk *clk;
@@ -222,7 +222,7 @@ err_out:
return ERR_PTR(ret);
}
-void __init mtk_clk_register_composites(const struct mtk_composite *mcs,
+void mtk_clk_register_composites(const struct mtk_composite *mcs,
int num, void __iomem *base, spinlock_t *lock,
struct clk_onecell_data *clk_data)
{
diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
index 966cab1..0c2deac 100644
--- a/drivers/clk/mediatek/clk-pll.c
+++ b/drivers/clk/mediatek/clk-pll.c
@@ -313,7 +313,7 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data,
return clk;
}
-void __init mtk_clk_register_plls(struct device_node *node,
+void mtk_clk_register_plls(struct device_node *node,
const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data)
{
void __iomem *base;
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 197e401..3495834 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -3,5 +3,5 @@
#
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o
-obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b-clkc.o
-obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o
+obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
+obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
index 53326c3..9bb70e7 100644
--- a/drivers/clk/meson/clkc.h
+++ b/drivers/clk/meson/clkc.h
@@ -98,7 +98,7 @@ struct meson_clk_mpll {
};
#define MESON_GATE(_name, _reg, _bit) \
-struct clk_gate gxbb_##_name = { \
+struct clk_gate _name = { \
.reg = (void __iomem *) _reg, \
.bit_idx = (_bit), \
.lock = &clk_lock, \
diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c
new file mode 100644
index 0000000..b45c5fb
--- /dev/null
+++ b/drivers/clk/meson/gxbb-aoclk.c
@@ -0,0 +1,191 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * BSD LICENSE
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/init.h>
+#include <dt-bindings/clock/gxbb-aoclkc.h>
+#include <dt-bindings/reset/gxbb-aoclkc.h>
+
+static DEFINE_SPINLOCK(gxbb_aoclk_lock);
+
+struct gxbb_aoclk_reset_controller {
+ struct reset_controller_dev reset;
+ unsigned int *data;
+ void __iomem *base;
+};
+
+static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gxbb_aoclk_reset_controller *reset =
+ container_of(rcdev, struct gxbb_aoclk_reset_controller, reset);
+
+ writel(BIT(reset->data[id]), reset->base);
+
+ return 0;
+}
+
+static const struct reset_control_ops gxbb_aoclk_reset_ops = {
+ .reset = gxbb_aoclk_do_reset,
+};
+
+#define GXBB_AO_GATE(_name, _bit) \
+static struct clk_gate _name##_ao = { \
+ .reg = (void __iomem *)0, \
+ .bit_idx = (_bit), \
+ .lock = &gxbb_aoclk_lock, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name "_ao", \
+ .ops = &clk_gate_ops, \
+ .parent_names = (const char *[]){ "clk81" }, \
+ .num_parents = 1, \
+ .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
+ }, \
+}
+
+GXBB_AO_GATE(remote, 0);
+GXBB_AO_GATE(i2c_master, 1);
+GXBB_AO_GATE(i2c_slave, 2);
+GXBB_AO_GATE(uart1, 3);
+GXBB_AO_GATE(uart2, 5);
+GXBB_AO_GATE(ir_blaster, 6);
+
+static unsigned int gxbb_aoclk_reset[] = {
+ [RESET_AO_REMOTE] = 16,
+ [RESET_AO_I2C_MASTER] = 18,
+ [RESET_AO_I2C_SLAVE] = 19,
+ [RESET_AO_UART1] = 17,
+ [RESET_AO_UART2] = 22,
+ [RESET_AO_IR_BLASTER] = 23,
+};
+
+static struct clk_gate *gxbb_aoclk_gate[] = {
+ [CLKID_AO_REMOTE] = &remote_ao,
+ [CLKID_AO_I2C_MASTER] = &i2c_master_ao,
+ [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao,
+ [CLKID_AO_UART1] = &uart1_ao,
+ [CLKID_AO_UART2] = &uart2_ao,
+ [CLKID_AO_IR_BLASTER] = &ir_blaster_ao,
+};
+
+static struct clk_hw_onecell_data gxbb_aoclk_onecell_data = {
+ .hws = {
+ [CLKID_AO_REMOTE] = &remote_ao.hw,
+ [CLKID_AO_I2C_MASTER] = &i2c_master_ao.hw,
+ [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao.hw,
+ [CLKID_AO_UART1] = &uart1_ao.hw,
+ [CLKID_AO_UART2] = &uart2_ao.hw,
+ [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw,
+ },
+ .num = ARRAY_SIZE(gxbb_aoclk_gate),
+};
+
+static int gxbb_aoclkc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *base;
+ int ret, clkid;
+ struct device *dev = &pdev->dev;
+ struct gxbb_aoclk_reset_controller *rstc;
+
+ rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL);
+ if (!rstc)
+ return -ENOMEM;
+
+ /* Generic clocks */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* Reset Controller */
+ rstc->base = base;
+ rstc->data = gxbb_aoclk_reset;
+ rstc->reset.ops = &gxbb_aoclk_reset_ops;
+ rstc->reset.nr_resets = ARRAY_SIZE(gxbb_aoclk_reset);
+ rstc->reset.of_node = dev->of_node;
+ ret = devm_reset_controller_register(dev, &rstc->reset);
+
+ /*
+ * Populate base address and register all clks
+ */
+ for (clkid = 0; clkid < gxbb_aoclk_onecell_data.num; clkid++) {
+ gxbb_aoclk_gate[clkid]->reg = base;
+
+ ret = devm_clk_hw_register(dev,
+ gxbb_aoclk_onecell_data.hws[clkid]);
+ if (ret)
+ return ret;
+ }
+
+ return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+ &gxbb_aoclk_onecell_data);
+}
+
+static const struct of_device_id gxbb_aoclkc_match_table[] = {
+ { .compatible = "amlogic,gxbb-aoclkc" },
+ { }
+};
+
+static struct platform_driver gxbb_aoclkc_driver = {
+ .probe = gxbb_aoclkc_probe,
+ .driver = {
+ .name = "gxbb-aoclkc",
+ .of_match_table = gxbb_aoclkc_match_table,
+ },
+};
+builtin_platform_driver(gxbb_aoclkc_driver);
diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c
index a4c6684b..9d9af44 100644
--- a/drivers/clk/meson/gxbb.c
+++ b/drivers/clk/meson/gxbb.c
@@ -565,90 +565,93 @@ static struct clk_gate gxbb_clk81 = {
};
/* Everything Else (EE) domain gates */
-static MESON_GATE(ddr, HHI_GCLK_MPEG0, 0);
-static MESON_GATE(dos, HHI_GCLK_MPEG0, 1);
-static MESON_GATE(isa, HHI_GCLK_MPEG0, 5);
-static MESON_GATE(pl301, HHI_GCLK_MPEG0, 6);
-static MESON_GATE(periphs, HHI_GCLK_MPEG0, 7);
-static MESON_GATE(spicc, HHI_GCLK_MPEG0, 8);
-static MESON_GATE(i2c, HHI_GCLK_MPEG0, 9);
-static MESON_GATE(sar_adc, HHI_GCLK_MPEG0, 10);
-static MESON_GATE(smart_card, HHI_GCLK_MPEG0, 11);
-static MESON_GATE(rng0, HHI_GCLK_MPEG0, 12);
-static MESON_GATE(uart0, HHI_GCLK_MPEG0, 13);
-static MESON_GATE(sdhc, HHI_GCLK_MPEG0, 14);
-static MESON_GATE(stream, HHI_GCLK_MPEG0, 15);
-static MESON_GATE(async_fifo, HHI_GCLK_MPEG0, 16);
-static MESON_GATE(sdio, HHI_GCLK_MPEG0, 17);
-static MESON_GATE(abuf, HHI_GCLK_MPEG0, 18);
-static MESON_GATE(hiu_iface, HHI_GCLK_MPEG0, 19);
-static MESON_GATE(assist_misc, HHI_GCLK_MPEG0, 23);
-static MESON_GATE(spi, HHI_GCLK_MPEG0, 30);
-
-static MESON_GATE(i2s_spdif, HHI_GCLK_MPEG1, 2);
-static MESON_GATE(eth, HHI_GCLK_MPEG1, 3);
-static MESON_GATE(demux, HHI_GCLK_MPEG1, 4);
-static MESON_GATE(aiu_glue, HHI_GCLK_MPEG1, 6);
-static MESON_GATE(iec958, HHI_GCLK_MPEG1, 7);
-static MESON_GATE(i2s_out, HHI_GCLK_MPEG1, 8);
-static MESON_GATE(amclk, HHI_GCLK_MPEG1, 9);
-static MESON_GATE(aififo2, HHI_GCLK_MPEG1, 10);
-static MESON_GATE(mixer, HHI_GCLK_MPEG1, 11);
-static MESON_GATE(mixer_iface, HHI_GCLK_MPEG1, 12);
-static MESON_GATE(adc, HHI_GCLK_MPEG1, 13);
-static MESON_GATE(blkmv, HHI_GCLK_MPEG1, 14);
-static MESON_GATE(aiu, HHI_GCLK_MPEG1, 15);
-static MESON_GATE(uart1, HHI_GCLK_MPEG1, 16);
-static MESON_GATE(g2d, HHI_GCLK_MPEG1, 20);
-static MESON_GATE(usb0, HHI_GCLK_MPEG1, 21);
-static MESON_GATE(usb1, HHI_GCLK_MPEG1, 22);
-static MESON_GATE(reset, HHI_GCLK_MPEG1, 23);
-static MESON_GATE(nand, HHI_GCLK_MPEG1, 24);
-static MESON_GATE(dos_parser, HHI_GCLK_MPEG1, 25);
-static MESON_GATE(usb, HHI_GCLK_MPEG1, 26);
-static MESON_GATE(vdin1, HHI_GCLK_MPEG1, 28);
-static MESON_GATE(ahb_arb0, HHI_GCLK_MPEG1, 29);
-static MESON_GATE(efuse, HHI_GCLK_MPEG1, 30);
-static MESON_GATE(boot_rom, HHI_GCLK_MPEG1, 31);
-
-static MESON_GATE(ahb_data_bus, HHI_GCLK_MPEG2, 1);
-static MESON_GATE(ahb_ctrl_bus, HHI_GCLK_MPEG2, 2);
-static MESON_GATE(hdmi_intr_sync, HHI_GCLK_MPEG2, 3);
-static MESON_GATE(hdmi_pclk, HHI_GCLK_MPEG2, 4);
-static MESON_GATE(usb1_ddr_bridge, HHI_GCLK_MPEG2, 8);
-static MESON_GATE(usb0_ddr_bridge, HHI_GCLK_MPEG2, 9);
-static MESON_GATE(mmc_pclk, HHI_GCLK_MPEG2, 11);
-static MESON_GATE(dvin, HHI_GCLK_MPEG2, 12);
-static MESON_GATE(uart2, HHI_GCLK_MPEG2, 15);
-static MESON_GATE(sana, HHI_GCLK_MPEG2, 22);
-static MESON_GATE(vpu_intr, HHI_GCLK_MPEG2, 25);
-static MESON_GATE(sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
-static MESON_GATE(clk81_a53, HHI_GCLK_MPEG2, 29);
-
-static MESON_GATE(vclk2_venci0, HHI_GCLK_OTHER, 1);
-static MESON_GATE(vclk2_venci1, HHI_GCLK_OTHER, 2);
-static MESON_GATE(vclk2_vencp0, HHI_GCLK_OTHER, 3);
-static MESON_GATE(vclk2_vencp1, HHI_GCLK_OTHER, 4);
-static MESON_GATE(gclk_venci_int0, HHI_GCLK_OTHER, 8);
-static MESON_GATE(gclk_vencp_int, HHI_GCLK_OTHER, 9);
-static MESON_GATE(dac_clk, HHI_GCLK_OTHER, 10);
-static MESON_GATE(aoclk_gate, HHI_GCLK_OTHER, 14);
-static MESON_GATE(iec958_gate, HHI_GCLK_OTHER, 16);
-static MESON_GATE(enc480p, HHI_GCLK_OTHER, 20);
-static MESON_GATE(rng1, HHI_GCLK_OTHER, 21);
-static MESON_GATE(gclk_venci_int1, HHI_GCLK_OTHER, 22);
-static MESON_GATE(vclk2_venclmcc, HHI_GCLK_OTHER, 24);
-static MESON_GATE(vclk2_vencl, HHI_GCLK_OTHER, 25);
-static MESON_GATE(vclk_other, HHI_GCLK_OTHER, 26);
-static MESON_GATE(edp, HHI_GCLK_OTHER, 31);
+static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0);
+static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1);
+static MESON_GATE(gxbb_isa, HHI_GCLK_MPEG0, 5);
+static MESON_GATE(gxbb_pl301, HHI_GCLK_MPEG0, 6);
+static MESON_GATE(gxbb_periphs, HHI_GCLK_MPEG0, 7);
+static MESON_GATE(gxbb_spicc, HHI_GCLK_MPEG0, 8);
+static MESON_GATE(gxbb_i2c, HHI_GCLK_MPEG0, 9);
+static MESON_GATE(gxbb_sar_adc, HHI_GCLK_MPEG0, 10);
+static MESON_GATE(gxbb_smart_card, HHI_GCLK_MPEG0, 11);
+static MESON_GATE(gxbb_rng0, HHI_GCLK_MPEG0, 12);
+static MESON_GATE(gxbb_uart0, HHI_GCLK_MPEG0, 13);
+static MESON_GATE(gxbb_sdhc, HHI_GCLK_MPEG0, 14);
+static MESON_GATE(gxbb_stream, HHI_GCLK_MPEG0, 15);
+static MESON_GATE(gxbb_async_fifo, HHI_GCLK_MPEG0, 16);
+static MESON_GATE(gxbb_sdio, HHI_GCLK_MPEG0, 17);
+static MESON_GATE(gxbb_abuf, HHI_GCLK_MPEG0, 18);
+static MESON_GATE(gxbb_hiu_iface, HHI_GCLK_MPEG0, 19);
+static MESON_GATE(gxbb_assist_misc, HHI_GCLK_MPEG0, 23);
+static MESON_GATE(gxbb_emmc_a, HHI_GCLK_MPEG0, 24);
+static MESON_GATE(gxbb_emmc_b, HHI_GCLK_MPEG0, 25);
+static MESON_GATE(gxbb_emmc_c, HHI_GCLK_MPEG0, 26);
+static MESON_GATE(gxbb_spi, HHI_GCLK_MPEG0, 30);
+
+static MESON_GATE(gxbb_i2s_spdif, HHI_GCLK_MPEG1, 2);
+static MESON_GATE(gxbb_eth, HHI_GCLK_MPEG1, 3);
+static MESON_GATE(gxbb_demux, HHI_GCLK_MPEG1, 4);
+static MESON_GATE(gxbb_aiu_glue, HHI_GCLK_MPEG1, 6);
+static MESON_GATE(gxbb_iec958, HHI_GCLK_MPEG1, 7);
+static MESON_GATE(gxbb_i2s_out, HHI_GCLK_MPEG1, 8);
+static MESON_GATE(gxbb_amclk, HHI_GCLK_MPEG1, 9);
+static MESON_GATE(gxbb_aififo2, HHI_GCLK_MPEG1, 10);
+static MESON_GATE(gxbb_mixer, HHI_GCLK_MPEG1, 11);
+static MESON_GATE(gxbb_mixer_iface, HHI_GCLK_MPEG1, 12);
+static MESON_GATE(gxbb_adc, HHI_GCLK_MPEG1, 13);
+static MESON_GATE(gxbb_blkmv, HHI_GCLK_MPEG1, 14);
+static MESON_GATE(gxbb_aiu, HHI_GCLK_MPEG1, 15);
+static MESON_GATE(gxbb_uart1, HHI_GCLK_MPEG1, 16);
+static MESON_GATE(gxbb_g2d, HHI_GCLK_MPEG1, 20);
+static MESON_GATE(gxbb_usb0, HHI_GCLK_MPEG1, 21);
+static MESON_GATE(gxbb_usb1, HHI_GCLK_MPEG1, 22);
+static MESON_GATE(gxbb_reset, HHI_GCLK_MPEG1, 23);
+static MESON_GATE(gxbb_nand, HHI_GCLK_MPEG1, 24);
+static MESON_GATE(gxbb_dos_parser, HHI_GCLK_MPEG1, 25);
+static MESON_GATE(gxbb_usb, HHI_GCLK_MPEG1, 26);
+static MESON_GATE(gxbb_vdin1, HHI_GCLK_MPEG1, 28);
+static MESON_GATE(gxbb_ahb_arb0, HHI_GCLK_MPEG1, 29);
+static MESON_GATE(gxbb_efuse, HHI_GCLK_MPEG1, 30);
+static MESON_GATE(gxbb_boot_rom, HHI_GCLK_MPEG1, 31);
+
+static MESON_GATE(gxbb_ahb_data_bus, HHI_GCLK_MPEG2, 1);
+static MESON_GATE(gxbb_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2);
+static MESON_GATE(gxbb_hdmi_intr_sync, HHI_GCLK_MPEG2, 3);
+static MESON_GATE(gxbb_hdmi_pclk, HHI_GCLK_MPEG2, 4);
+static MESON_GATE(gxbb_usb1_ddr_bridge, HHI_GCLK_MPEG2, 8);
+static MESON_GATE(gxbb_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9);
+static MESON_GATE(gxbb_mmc_pclk, HHI_GCLK_MPEG2, 11);
+static MESON_GATE(gxbb_dvin, HHI_GCLK_MPEG2, 12);
+static MESON_GATE(gxbb_uart2, HHI_GCLK_MPEG2, 15);
+static MESON_GATE(gxbb_sana, HHI_GCLK_MPEG2, 22);
+static MESON_GATE(gxbb_vpu_intr, HHI_GCLK_MPEG2, 25);
+static MESON_GATE(gxbb_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
+static MESON_GATE(gxbb_clk81_a53, HHI_GCLK_MPEG2, 29);
+
+static MESON_GATE(gxbb_vclk2_venci0, HHI_GCLK_OTHER, 1);
+static MESON_GATE(gxbb_vclk2_venci1, HHI_GCLK_OTHER, 2);
+static MESON_GATE(gxbb_vclk2_vencp0, HHI_GCLK_OTHER, 3);
+static MESON_GATE(gxbb_vclk2_vencp1, HHI_GCLK_OTHER, 4);
+static MESON_GATE(gxbb_gclk_venci_int0, HHI_GCLK_OTHER, 8);
+static MESON_GATE(gxbb_gclk_vencp_int, HHI_GCLK_OTHER, 9);
+static MESON_GATE(gxbb_dac_clk, HHI_GCLK_OTHER, 10);
+static MESON_GATE(gxbb_aoclk_gate, HHI_GCLK_OTHER, 14);
+static MESON_GATE(gxbb_iec958_gate, HHI_GCLK_OTHER, 16);
+static MESON_GATE(gxbb_enc480p, HHI_GCLK_OTHER, 20);
+static MESON_GATE(gxbb_rng1, HHI_GCLK_OTHER, 21);
+static MESON_GATE(gxbb_gclk_venci_int1, HHI_GCLK_OTHER, 22);
+static MESON_GATE(gxbb_vclk2_venclmcc, HHI_GCLK_OTHER, 24);
+static MESON_GATE(gxbb_vclk2_vencl, HHI_GCLK_OTHER, 25);
+static MESON_GATE(gxbb_vclk_other, HHI_GCLK_OTHER, 26);
+static MESON_GATE(gxbb_edp, HHI_GCLK_OTHER, 31);
/* Always On (AO) domain gates */
-static MESON_GATE(ao_media_cpu, HHI_GCLK_AO, 0);
-static MESON_GATE(ao_ahb_sram, HHI_GCLK_AO, 1);
-static MESON_GATE(ao_ahb_bus, HHI_GCLK_AO, 2);
-static MESON_GATE(ao_iface, HHI_GCLK_AO, 3);
-static MESON_GATE(ao_i2c, HHI_GCLK_AO, 4);
+static MESON_GATE(gxbb_ao_media_cpu, HHI_GCLK_AO, 0);
+static MESON_GATE(gxbb_ao_ahb_sram, HHI_GCLK_AO, 1);
+static MESON_GATE(gxbb_ao_ahb_bus, HHI_GCLK_AO, 2);
+static MESON_GATE(gxbb_ao_iface, HHI_GCLK_AO, 3);
+static MESON_GATE(gxbb_ao_i2c, HHI_GCLK_AO, 4);
/* Array of all clocks provided by this provider */
@@ -748,6 +751,9 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = {
[CLKID_AO_AHB_BUS] = &gxbb_ao_ahb_bus.hw,
[CLKID_AO_IFACE] = &gxbb_ao_iface.hw,
[CLKID_AO_I2C] = &gxbb_ao_i2c.hw,
+ [CLKID_SD_EMMC_A] = &gxbb_emmc_a.hw,
+ [CLKID_SD_EMMC_B] = &gxbb_emmc_b.hw,
+ [CLKID_SD_EMMC_C] = &gxbb_emmc_c.hw,
},
.num = NR_CLKS,
};
@@ -847,6 +853,9 @@ static struct clk_gate *gxbb_clk_gates[] = {
&gxbb_ao_ahb_bus,
&gxbb_ao_iface,
&gxbb_ao_i2c,
+ &gxbb_emmc_a,
+ &gxbb_emmc_b,
+ &gxbb_emmc_c,
};
static int gxbb_clkc_probe(struct platform_device *pdev)
@@ -937,8 +946,4 @@ static struct platform_driver gxbb_driver = {
},
};
-static int __init gxbb_clkc_init(void)
-{
- return platform_driver_register(&gxbb_driver);
-}
-device_initcall(gxbb_clkc_init);
+builtin_platform_driver(gxbb_driver);
diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h
index a2adf34..ae461b1 100644
--- a/drivers/clk/meson/gxbb.h
+++ b/drivers/clk/meson/gxbb.h
@@ -170,11 +170,11 @@
*/
#define CLKID_SYS_PLL 0
/* CLKID_CPUCLK */
-#define CLKID_HDMI_PLL 2
+/* CLKID_HDMI_PLL */
#define CLKID_FIXED_PLL 3
-#define CLKID_FCLK_DIV2 4
-#define CLKID_FCLK_DIV3 5
-#define CLKID_FCLK_DIV4 6
+/* CLKID_FCLK_DIV2 */
+/* CLKID_FCLK_DIV3 */
+/* CLKID_FCLK_DIV4 */
#define CLKID_FCLK_DIV5 7
#define CLKID_FCLK_DIV7 8
#define CLKID_GP0_PLL 9
@@ -262,8 +262,11 @@
#define CLKID_AO_AHB_BUS 91
#define CLKID_AO_IFACE 92
#define CLKID_AO_I2C 93
+/* CLKID_SD_EMMC_A */
+/* CLKID_SD_EMMC_B */
+/* CLKID_SD_EMMC_C */
-#define NR_CLKS 94
+#define NR_CLKS 97
/* include the CLKIDs that have been made part of the stable DT binding */
#include <dt-bindings/clock/gxbb-clkc.h>
diff --git a/drivers/clk/meson/meson8b-clkc.c b/drivers/clk/meson/meson8b.c
index 4c9413c..3f1be46 100644
--- a/drivers/clk/meson/meson8b-clkc.c
+++ b/drivers/clk/meson/meson8b.c
@@ -23,27 +23,11 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
-#include <dt-bindings/clock/meson8b-clkc.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include "clkc.h"
-
-/*
- * Clock controller register offsets
- *
- * Register offsets from the HardKernel[0] data sheet are listed in comment
- * blocks below. Those offsets must be multiplied by 4 before adding them to
- * the base address to get the right value
- *
- * [0] http://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf
- */
-#define MESON8B_REG_SYS_CPU_CNTL1 0x015c /* 0x57 offset in data sheet */
-#define MESON8B_REG_HHI_MPEG 0x0174 /* 0x5d offset in data sheet */
-#define MESON8B_REG_MALI 0x01b0 /* 0x6c offset in data sheet */
-#define MESON8B_REG_PLL_FIXED 0x0280
-#define MESON8B_REG_PLL_SYS 0x0300
-#define MESON8B_REG_PLL_VID 0x0320
+#include "meson8b.h"
static DEFINE_SPINLOCK(clk_lock);
@@ -128,17 +112,17 @@ static struct clk_fixed_rate meson8b_xtal = {
static struct meson_clk_pll meson8b_fixed_pll = {
.m = {
- .reg_off = MESON8B_REG_PLL_FIXED,
+ .reg_off = HHI_MPLL_CNTL,
.shift = 0,
.width = 9,
},
.n = {
- .reg_off = MESON8B_REG_PLL_FIXED,
+ .reg_off = HHI_MPLL_CNTL,
.shift = 9,
.width = 5,
},
.od = {
- .reg_off = MESON8B_REG_PLL_FIXED,
+ .reg_off = HHI_MPLL_CNTL,
.shift = 16,
.width = 2,
},
@@ -154,17 +138,17 @@ static struct meson_clk_pll meson8b_fixed_pll = {
static struct meson_clk_pll meson8b_vid_pll = {
.m = {
- .reg_off = MESON8B_REG_PLL_VID,
+ .reg_off = HHI_VID_PLL_CNTL,
.shift = 0,
.width = 9,
},
.n = {
- .reg_off = MESON8B_REG_PLL_VID,
+ .reg_off = HHI_VID_PLL_CNTL,
.shift = 9,
.width = 5,
},
.od = {
- .reg_off = MESON8B_REG_PLL_VID,
+ .reg_off = HHI_VID_PLL_CNTL,
.shift = 16,
.width = 2,
},
@@ -180,17 +164,17 @@ static struct meson_clk_pll meson8b_vid_pll = {
static struct meson_clk_pll meson8b_sys_pll = {
.m = {
- .reg_off = MESON8B_REG_PLL_SYS,
+ .reg_off = HHI_SYS_PLL_CNTL,
.shift = 0,
.width = 9,
},
.n = {
- .reg_off = MESON8B_REG_PLL_SYS,
+ .reg_off = HHI_SYS_PLL_CNTL,
.shift = 9,
.width = 5,
},
.od = {
- .reg_off = MESON8B_REG_PLL_SYS,
+ .reg_off = HHI_SYS_PLL_CNTL,
.shift = 16,
.width = 2,
},
@@ -267,7 +251,7 @@ static struct clk_fixed_factor meson8b_fclk_div7 = {
* forthcoming coordinated clock rates feature
*/
static struct meson_clk_cpu meson8b_cpu_clk = {
- .reg_off = MESON8B_REG_SYS_CPU_CNTL1,
+ .reg_off = HHI_SYS_CPU_CLK_CNTL1,
.div_table = cpu_div_table,
.clk_nb.notifier_call = meson_clk_cpu_notifier_cb,
.hw.init = &(struct clk_init_data){
@@ -281,7 +265,7 @@ static struct meson_clk_cpu meson8b_cpu_clk = {
static u32 mux_table_clk81[] = { 6, 5, 7 };
struct clk_mux meson8b_mpeg_clk_sel = {
- .reg = (void *)MESON8B_REG_HHI_MPEG,
+ .reg = (void *)HHI_MPEG_CLK_CNTL,
.mask = 0x7,
.shift = 12,
.flags = CLK_MUX_READ_ONLY,
@@ -303,7 +287,7 @@ struct clk_mux meson8b_mpeg_clk_sel = {
};
struct clk_divider meson8b_mpeg_clk_div = {
- .reg = (void *)MESON8B_REG_HHI_MPEG,
+ .reg = (void *)HHI_MPEG_CLK_CNTL,
.shift = 0,
.width = 7,
.lock = &clk_lock,
@@ -317,7 +301,7 @@ struct clk_divider meson8b_mpeg_clk_div = {
};
struct clk_gate meson8b_clk81 = {
- .reg = (void *)MESON8B_REG_HHI_MPEG,
+ .reg = (void *)HHI_MPEG_CLK_CNTL,
.bit_idx = 7,
.lock = &clk_lock,
.hw.init = &(struct clk_init_data){
@@ -329,6 +313,92 @@ struct clk_gate meson8b_clk81 = {
},
};
+/* Everything Else (EE) domain gates */
+
+static MESON_GATE(meson8b_ddr, HHI_GCLK_MPEG0, 0);
+static MESON_GATE(meson8b_dos, HHI_GCLK_MPEG0, 1);
+static MESON_GATE(meson8b_isa, HHI_GCLK_MPEG0, 5);
+static MESON_GATE(meson8b_pl301, HHI_GCLK_MPEG0, 6);
+static MESON_GATE(meson8b_periphs, HHI_GCLK_MPEG0, 7);
+static MESON_GATE(meson8b_spicc, HHI_GCLK_MPEG0, 8);
+static MESON_GATE(meson8b_i2c, HHI_GCLK_MPEG0, 9);
+static MESON_GATE(meson8b_sar_adc, HHI_GCLK_MPEG0, 10);
+static MESON_GATE(meson8b_smart_card, HHI_GCLK_MPEG0, 11);
+static MESON_GATE(meson8b_rng0, HHI_GCLK_MPEG0, 12);
+static MESON_GATE(meson8b_uart0, HHI_GCLK_MPEG0, 13);
+static MESON_GATE(meson8b_sdhc, HHI_GCLK_MPEG0, 14);
+static MESON_GATE(meson8b_stream, HHI_GCLK_MPEG0, 15);
+static MESON_GATE(meson8b_async_fifo, HHI_GCLK_MPEG0, 16);
+static MESON_GATE(meson8b_sdio, HHI_GCLK_MPEG0, 17);
+static MESON_GATE(meson8b_abuf, HHI_GCLK_MPEG0, 18);
+static MESON_GATE(meson8b_hiu_iface, HHI_GCLK_MPEG0, 19);
+static MESON_GATE(meson8b_assist_misc, HHI_GCLK_MPEG0, 23);
+static MESON_GATE(meson8b_spi, HHI_GCLK_MPEG0, 30);
+
+static MESON_GATE(meson8b_i2s_spdif, HHI_GCLK_MPEG1, 2);
+static MESON_GATE(meson8b_eth, HHI_GCLK_MPEG1, 3);
+static MESON_GATE(meson8b_demux, HHI_GCLK_MPEG1, 4);
+static MESON_GATE(meson8b_aiu_glue, HHI_GCLK_MPEG1, 6);
+static MESON_GATE(meson8b_iec958, HHI_GCLK_MPEG1, 7);
+static MESON_GATE(meson8b_i2s_out, HHI_GCLK_MPEG1, 8);
+static MESON_GATE(meson8b_amclk, HHI_GCLK_MPEG1, 9);
+static MESON_GATE(meson8b_aififo2, HHI_GCLK_MPEG1, 10);
+static MESON_GATE(meson8b_mixer, HHI_GCLK_MPEG1, 11);
+static MESON_GATE(meson8b_mixer_iface, HHI_GCLK_MPEG1, 12);
+static MESON_GATE(meson8b_adc, HHI_GCLK_MPEG1, 13);
+static MESON_GATE(meson8b_blkmv, HHI_GCLK_MPEG1, 14);
+static MESON_GATE(meson8b_aiu, HHI_GCLK_MPEG1, 15);
+static MESON_GATE(meson8b_uart1, HHI_GCLK_MPEG1, 16);
+static MESON_GATE(meson8b_g2d, HHI_GCLK_MPEG1, 20);
+static MESON_GATE(meson8b_usb0, HHI_GCLK_MPEG1, 21);
+static MESON_GATE(meson8b_usb1, HHI_GCLK_MPEG1, 22);
+static MESON_GATE(meson8b_reset, HHI_GCLK_MPEG1, 23);
+static MESON_GATE(meson8b_nand, HHI_GCLK_MPEG1, 24);
+static MESON_GATE(meson8b_dos_parser, HHI_GCLK_MPEG1, 25);
+static MESON_GATE(meson8b_usb, HHI_GCLK_MPEG1, 26);
+static MESON_GATE(meson8b_vdin1, HHI_GCLK_MPEG1, 28);
+static MESON_GATE(meson8b_ahb_arb0, HHI_GCLK_MPEG1, 29);
+static MESON_GATE(meson8b_efuse, HHI_GCLK_MPEG1, 30);
+static MESON_GATE(meson8b_boot_rom, HHI_GCLK_MPEG1, 31);
+
+static MESON_GATE(meson8b_ahb_data_bus, HHI_GCLK_MPEG2, 1);
+static MESON_GATE(meson8b_ahb_ctrl_bus, HHI_GCLK_MPEG2, 2);
+static MESON_GATE(meson8b_hdmi_intr_sync, HHI_GCLK_MPEG2, 3);
+static MESON_GATE(meson8b_hdmi_pclk, HHI_GCLK_MPEG2, 4);
+static MESON_GATE(meson8b_usb1_ddr_bridge, HHI_GCLK_MPEG2, 8);
+static MESON_GATE(meson8b_usb0_ddr_bridge, HHI_GCLK_MPEG2, 9);
+static MESON_GATE(meson8b_mmc_pclk, HHI_GCLK_MPEG2, 11);
+static MESON_GATE(meson8b_dvin, HHI_GCLK_MPEG2, 12);
+static MESON_GATE(meson8b_uart2, HHI_GCLK_MPEG2, 15);
+static MESON_GATE(meson8b_sana, HHI_GCLK_MPEG2, 22);
+static MESON_GATE(meson8b_vpu_intr, HHI_GCLK_MPEG2, 25);
+static MESON_GATE(meson8b_sec_ahb_ahb3_bridge, HHI_GCLK_MPEG2, 26);
+static MESON_GATE(meson8b_clk81_a9, HHI_GCLK_MPEG2, 29);
+
+static MESON_GATE(meson8b_vclk2_venci0, HHI_GCLK_OTHER, 1);
+static MESON_GATE(meson8b_vclk2_venci1, HHI_GCLK_OTHER, 2);
+static MESON_GATE(meson8b_vclk2_vencp0, HHI_GCLK_OTHER, 3);
+static MESON_GATE(meson8b_vclk2_vencp1, HHI_GCLK_OTHER, 4);
+static MESON_GATE(meson8b_gclk_venci_int, HHI_GCLK_OTHER, 8);
+static MESON_GATE(meson8b_gclk_vencp_int, HHI_GCLK_OTHER, 9);
+static MESON_GATE(meson8b_dac_clk, HHI_GCLK_OTHER, 10);
+static MESON_GATE(meson8b_aoclk_gate, HHI_GCLK_OTHER, 14);
+static MESON_GATE(meson8b_iec958_gate, HHI_GCLK_OTHER, 16);
+static MESON_GATE(meson8b_enc480p, HHI_GCLK_OTHER, 20);
+static MESON_GATE(meson8b_rng1, HHI_GCLK_OTHER, 21);
+static MESON_GATE(meson8b_gclk_vencl_int, HHI_GCLK_OTHER, 22);
+static MESON_GATE(meson8b_vclk2_venclmcc, HHI_GCLK_OTHER, 24);
+static MESON_GATE(meson8b_vclk2_vencl, HHI_GCLK_OTHER, 25);
+static MESON_GATE(meson8b_vclk2_other, HHI_GCLK_OTHER, 26);
+static MESON_GATE(meson8b_edp, HHI_GCLK_OTHER, 31);
+
+/* Always On (AO) domain gates */
+
+static MESON_GATE(meson8b_ao_media_cpu, HHI_GCLK_AO, 0);
+static MESON_GATE(meson8b_ao_ahb_sram, HHI_GCLK_AO, 1);
+static MESON_GATE(meson8b_ao_ahb_bus, HHI_GCLK_AO, 2);
+static MESON_GATE(meson8b_ao_iface, HHI_GCLK_AO, 3);
+
static struct clk_hw_onecell_data meson8b_hw_onecell_data = {
.hws = {
[CLKID_XTAL] = &meson8b_xtal.hw,
@@ -344,6 +414,83 @@ static struct clk_hw_onecell_data meson8b_hw_onecell_data = {
[CLKID_MPEG_SEL] = &meson8b_mpeg_clk_sel.hw,
[CLKID_MPEG_DIV] = &meson8b_mpeg_clk_div.hw,
[CLKID_CLK81] = &meson8b_clk81.hw,
+ [CLKID_DDR] = &meson8b_ddr.hw,
+ [CLKID_DOS] = &meson8b_dos.hw,
+ [CLKID_ISA] = &meson8b_isa.hw,
+ [CLKID_PL301] = &meson8b_pl301.hw,
+ [CLKID_PERIPHS] = &meson8b_periphs.hw,
+ [CLKID_SPICC] = &meson8b_spicc.hw,
+ [CLKID_I2C] = &meson8b_i2c.hw,
+ [CLKID_SAR_ADC] = &meson8b_sar_adc.hw,
+ [CLKID_SMART_CARD] = &meson8b_smart_card.hw,
+ [CLKID_RNG0] = &meson8b_rng0.hw,
+ [CLKID_UART0] = &meson8b_uart0.hw,
+ [CLKID_SDHC] = &meson8b_sdhc.hw,
+ [CLKID_STREAM] = &meson8b_stream.hw,
+ [CLKID_ASYNC_FIFO] = &meson8b_async_fifo.hw,
+ [CLKID_SDIO] = &meson8b_sdio.hw,
+ [CLKID_ABUF] = &meson8b_abuf.hw,
+ [CLKID_HIU_IFACE] = &meson8b_hiu_iface.hw,
+ [CLKID_ASSIST_MISC] = &meson8b_assist_misc.hw,
+ [CLKID_SPI] = &meson8b_spi.hw,
+ [CLKID_I2S_SPDIF] = &meson8b_i2s_spdif.hw,
+ [CLKID_ETH] = &meson8b_eth.hw,
+ [CLKID_DEMUX] = &meson8b_demux.hw,
+ [CLKID_AIU_GLUE] = &meson8b_aiu_glue.hw,
+ [CLKID_IEC958] = &meson8b_iec958.hw,
+ [CLKID_I2S_OUT] = &meson8b_i2s_out.hw,
+ [CLKID_AMCLK] = &meson8b_amclk.hw,
+ [CLKID_AIFIFO2] = &meson8b_aififo2.hw,
+ [CLKID_MIXER] = &meson8b_mixer.hw,
+ [CLKID_MIXER_IFACE] = &meson8b_mixer_iface.hw,
+ [CLKID_ADC] = &meson8b_adc.hw,
+ [CLKID_BLKMV] = &meson8b_blkmv.hw,
+ [CLKID_AIU] = &meson8b_aiu.hw,
+ [CLKID_UART1] = &meson8b_uart1.hw,
+ [CLKID_G2D] = &meson8b_g2d.hw,
+ [CLKID_USB0] = &meson8b_usb0.hw,
+ [CLKID_USB1] = &meson8b_usb1.hw,
+ [CLKID_RESET] = &meson8b_reset.hw,
+ [CLKID_NAND] = &meson8b_nand.hw,
+ [CLKID_DOS_PARSER] = &meson8b_dos_parser.hw,
+ [CLKID_USB] = &meson8b_usb.hw,
+ [CLKID_VDIN1] = &meson8b_vdin1.hw,
+ [CLKID_AHB_ARB0] = &meson8b_ahb_arb0.hw,
+ [CLKID_EFUSE] = &meson8b_efuse.hw,
+ [CLKID_BOOT_ROM] = &meson8b_boot_rom.hw,
+ [CLKID_AHB_DATA_BUS] = &meson8b_ahb_data_bus.hw,
+ [CLKID_AHB_CTRL_BUS] = &meson8b_ahb_ctrl_bus.hw,
+ [CLKID_HDMI_INTR_SYNC] = &meson8b_hdmi_intr_sync.hw,
+ [CLKID_HDMI_PCLK] = &meson8b_hdmi_pclk.hw,
+ [CLKID_USB1_DDR_BRIDGE] = &meson8b_usb1_ddr_bridge.hw,
+ [CLKID_USB0_DDR_BRIDGE] = &meson8b_usb0_ddr_bridge.hw,
+ [CLKID_MMC_PCLK] = &meson8b_mmc_pclk.hw,
+ [CLKID_DVIN] = &meson8b_dvin.hw,
+ [CLKID_UART2] = &meson8b_uart2.hw,
+ [CLKID_SANA] = &meson8b_sana.hw,
+ [CLKID_VPU_INTR] = &meson8b_vpu_intr.hw,
+ [CLKID_SEC_AHB_AHB3_BRIDGE] = &meson8b_sec_ahb_ahb3_bridge.hw,
+ [CLKID_CLK81_A9] = &meson8b_clk81_a9.hw,
+ [CLKID_VCLK2_VENCI0] = &meson8b_vclk2_venci0.hw,
+ [CLKID_VCLK2_VENCI1] = &meson8b_vclk2_venci1.hw,
+ [CLKID_VCLK2_VENCP0] = &meson8b_vclk2_vencp0.hw,
+ [CLKID_VCLK2_VENCP1] = &meson8b_vclk2_vencp1.hw,
+ [CLKID_GCLK_VENCI_INT] = &meson8b_gclk_venci_int.hw,
+ [CLKID_GCLK_VENCP_INT] = &meson8b_gclk_vencp_int.hw,
+ [CLKID_DAC_CLK] = &meson8b_dac_clk.hw,
+ [CLKID_AOCLK_GATE] = &meson8b_aoclk_gate.hw,
+ [CLKID_IEC958_GATE] = &meson8b_iec958_gate.hw,
+ [CLKID_ENC480P] = &meson8b_enc480p.hw,
+ [CLKID_RNG1] = &meson8b_rng1.hw,
+ [CLKID_GCLK_VENCL_INT] = &meson8b_gclk_vencl_int.hw,
+ [CLKID_VCLK2_VENCLMCC] = &meson8b_vclk2_venclmcc.hw,
+ [CLKID_VCLK2_VENCL] = &meson8b_vclk2_vencl.hw,
+ [CLKID_VCLK2_OTHER] = &meson8b_vclk2_other.hw,
+ [CLKID_EDP] = &meson8b_edp.hw,
+ [CLKID_AO_MEDIA_CPU] = &meson8b_ao_media_cpu.hw,
+ [CLKID_AO_AHB_SRAM] = &meson8b_ao_ahb_sram.hw,
+ [CLKID_AO_AHB_BUS] = &meson8b_ao_ahb_bus.hw,
+ [CLKID_AO_IFACE] = &meson8b_ao_iface.hw,
},
.num = CLK_NR_CLKS,
};
@@ -354,6 +501,87 @@ static struct meson_clk_pll *const meson8b_clk_plls[] = {
&meson8b_sys_pll,
};
+static struct clk_gate *meson8b_clk_gates[] = {
+ &meson8b_clk81,
+ &meson8b_ddr,
+ &meson8b_dos,
+ &meson8b_isa,
+ &meson8b_pl301,
+ &meson8b_periphs,
+ &meson8b_spicc,
+ &meson8b_i2c,
+ &meson8b_sar_adc,
+ &meson8b_smart_card,
+ &meson8b_rng0,
+ &meson8b_uart0,
+ &meson8b_sdhc,
+ &meson8b_stream,
+ &meson8b_async_fifo,
+ &meson8b_sdio,
+ &meson8b_abuf,
+ &meson8b_hiu_iface,
+ &meson8b_assist_misc,
+ &meson8b_spi,
+ &meson8b_i2s_spdif,
+ &meson8b_eth,
+ &meson8b_demux,
+ &meson8b_aiu_glue,
+ &meson8b_iec958,
+ &meson8b_i2s_out,
+ &meson8b_amclk,
+ &meson8b_aififo2,
+ &meson8b_mixer,
+ &meson8b_mixer_iface,
+ &meson8b_adc,
+ &meson8b_blkmv,
+ &meson8b_aiu,
+ &meson8b_uart1,
+ &meson8b_g2d,
+ &meson8b_usb0,
+ &meson8b_usb1,
+ &meson8b_reset,
+ &meson8b_nand,
+ &meson8b_dos_parser,
+ &meson8b_usb,
+ &meson8b_vdin1,
+ &meson8b_ahb_arb0,
+ &meson8b_efuse,
+ &meson8b_boot_rom,
+ &meson8b_ahb_data_bus,
+ &meson8b_ahb_ctrl_bus,
+ &meson8b_hdmi_intr_sync,
+ &meson8b_hdmi_pclk,
+ &meson8b_usb1_ddr_bridge,
+ &meson8b_usb0_ddr_bridge,
+ &meson8b_mmc_pclk,
+ &meson8b_dvin,
+ &meson8b_uart2,
+ &meson8b_sana,
+ &meson8b_vpu_intr,
+ &meson8b_sec_ahb_ahb3_bridge,
+ &meson8b_clk81_a9,
+ &meson8b_vclk2_venci0,
+ &meson8b_vclk2_venci1,
+ &meson8b_vclk2_vencp0,
+ &meson8b_vclk2_vencp1,
+ &meson8b_gclk_venci_int,
+ &meson8b_gclk_vencp_int,
+ &meson8b_dac_clk,
+ &meson8b_aoclk_gate,
+ &meson8b_iec958_gate,
+ &meson8b_enc480p,
+ &meson8b_rng1,
+ &meson8b_gclk_vencl_int,
+ &meson8b_vclk2_venclmcc,
+ &meson8b_vclk2_vencl,
+ &meson8b_vclk2_other,
+ &meson8b_edp,
+ &meson8b_ao_media_cpu,
+ &meson8b_ao_ahb_sram,
+ &meson8b_ao_ahb_bus,
+ &meson8b_ao_iface,
+};
+
static int meson8b_clkc_probe(struct platform_device *pdev)
{
void __iomem *clk_base;
@@ -381,6 +609,11 @@ static int meson8b_clkc_probe(struct platform_device *pdev)
meson8b_mpeg_clk_div.reg = clk_base + (u32)meson8b_mpeg_clk_div.reg;
meson8b_clk81.reg = clk_base + (u32)meson8b_clk81.reg;
+ /* Populate base address for gates */
+ for (i = 0; i < ARRAY_SIZE(meson8b_clk_gates); i++)
+ meson8b_clk_gates[i]->reg = clk_base +
+ (u32)meson8b_clk_gates[i]->reg;
+
/*
* register all clks
* CLKID_UNUSED = 0, so skip it and start with CLKID_XTAL = 1
@@ -440,8 +673,4 @@ static struct platform_driver meson8b_driver = {
},
};
-static int __init meson8b_clkc_init(void)
-{
- return platform_driver_register(&meson8b_driver);
-}
-device_initcall(meson8b_clkc_init);
+builtin_platform_driver(meson8b_driver);
diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h
new file mode 100644
index 0000000..010e958
--- /dev/null
+++ b/drivers/clk/meson/meson8b.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2015 Endless Mobile, Inc.
+ * Author: Carlo Caione <carlo@endlessm.com>
+ *
+ * Copyright (c) 2016 BayLibre, Inc.
+ * Michael Turquette <mturquette@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MESON8B_H
+#define __MESON8B_H
+
+/*
+ * Clock controller register offsets
+ *
+ * Register offsets from the HardKernel[0] data sheet are listed in comment
+ * blocks below. Those offsets must be multiplied by 4 before adding them to
+ * the base address to get the right value
+ *
+ * [0] http://dn.odroid.com/S805/Datasheet/S805_Datasheet%20V0.8%2020150126.pdf
+ */
+#define HHI_GCLK_MPEG0 0x140 /* 0x50 offset in data sheet */
+#define HHI_GCLK_MPEG1 0x144 /* 0x51 offset in data sheet */
+#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */
+#define HHI_GCLK_OTHER 0x150 /* 0x54 offset in data sheet */
+#define HHI_GCLK_AO 0x154 /* 0x55 offset in data sheet */
+#define HHI_SYS_CPU_CLK_CNTL1 0x15c /* 0x57 offset in data sheet */
+#define HHI_MPEG_CLK_CNTL 0x174 /* 0x5d offset in data sheet */
+#define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */
+#define HHI_SYS_PLL_CNTL 0x300 /* 0xc0 offset in data sheet */
+#define HHI_VID_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */
+
+/*
+ * CLKID index values
+ *
+ * These indices are entirely contrived and do not map onto the hardware.
+ * Migrate them out of this header and into the DT header file when they need
+ * to be exposed to client nodes in DT: include/dt-bindings/clock/meson8b-clkc.h
+ */
+
+/* CLKID_UNUSED */
+/* CLKID_XTAL */
+/* CLKID_PLL_FIXED */
+/* CLKID_PLL_VID */
+/* CLKID_PLL_SYS */
+/* CLKID_FCLK_DIV2 */
+/* CLKID_FCLK_DIV3 */
+/* CLKID_FCLK_DIV4 */
+/* CLKID_FCLK_DIV5 */
+/* CLKID_FCLK_DIV7 */
+/* CLKID_CLK81 */
+/* CLKID_MALI */
+/* CLKID_CPUCLK */
+/* CLKID_ZERO */
+/* CLKID_MPEG_SEL */
+/* CLKID_MPEG_DIV */
+#define CLKID_DDR 16
+#define CLKID_DOS 17
+#define CLKID_ISA 18
+#define CLKID_PL301 19
+#define CLKID_PERIPHS 20
+#define CLKID_SPICC 21
+#define CLKID_I2C 22
+#define CLKID_SAR_ADC 23
+#define CLKID_SMART_CARD 24
+#define CLKID_RNG0 25
+#define CLKID_UART0 26
+#define CLKID_SDHC 27
+#define CLKID_STREAM 28
+#define CLKID_ASYNC_FIFO 29
+#define CLKID_SDIO 30
+#define CLKID_ABUF 31
+#define CLKID_HIU_IFACE 32
+#define CLKID_ASSIST_MISC 33
+#define CLKID_SPI 34
+#define CLKID_I2S_SPDIF 35
+#define CLKID_ETH 36
+#define CLKID_DEMUX 37
+#define CLKID_AIU_GLUE 38
+#define CLKID_IEC958 39
+#define CLKID_I2S_OUT 40
+#define CLKID_AMCLK 41
+#define CLKID_AIFIFO2 42
+#define CLKID_MIXER 43
+#define CLKID_MIXER_IFACE 44
+#define CLKID_ADC 45
+#define CLKID_BLKMV 46
+#define CLKID_AIU 47
+#define CLKID_UART1 48
+#define CLKID_G2D 49
+#define CLKID_USB0 50
+#define CLKID_USB1 51
+#define CLKID_RESET 52
+#define CLKID_NAND 53
+#define CLKID_DOS_PARSER 54
+#define CLKID_USB 55
+#define CLKID_VDIN1 56
+#define CLKID_AHB_ARB0 57
+#define CLKID_EFUSE 58
+#define CLKID_BOOT_ROM 59
+#define CLKID_AHB_DATA_BUS 60
+#define CLKID_AHB_CTRL_BUS 61
+#define CLKID_HDMI_INTR_SYNC 62
+#define CLKID_HDMI_PCLK 63
+#define CLKID_USB1_DDR_BRIDGE 64
+#define CLKID_USB0_DDR_BRIDGE 65
+#define CLKID_MMC_PCLK 66
+#define CLKID_DVIN 67
+#define CLKID_UART2 68
+#define CLKID_SANA 69
+#define CLKID_VPU_INTR 70
+#define CLKID_SEC_AHB_AHB3_BRIDGE 71
+#define CLKID_CLK81_A9 72
+#define CLKID_VCLK2_VENCI0 73
+#define CLKID_VCLK2_VENCI1 74
+#define CLKID_VCLK2_VENCP0 75
+#define CLKID_VCLK2_VENCP1 76
+#define CLKID_GCLK_VENCI_INT 77
+#define CLKID_GCLK_VENCP_INT 78
+#define CLKID_DAC_CLK 79
+#define CLKID_AOCLK_GATE 80
+#define CLKID_IEC958_GATE 81
+#define CLKID_ENC480P 82
+#define CLKID_RNG1 83
+#define CLKID_GCLK_VENCL_INT 84
+#define CLKID_VCLK2_VENCLMCC 85
+#define CLKID_VCLK2_VENCL 86
+#define CLKID_VCLK2_OTHER 87
+#define CLKID_EDP 88
+#define CLKID_AO_MEDIA_CPU 89
+#define CLKID_AO_AHB_SRAM 90
+#define CLKID_AO_AHB_BUS 91
+#define CLKID_AO_IFACE 92
+
+#define CLK_NR_CLKS 93
+
+/* include the CLKIDs that have been made part of the stable DT binding */
+#include <dt-bindings/clock/meson8b-clkc.h>
+
+#endif /* __MESON8B_H */
diff --git a/drivers/clk/microchip/clk-core.c b/drivers/clk/microchip/clk-core.c
index ca85cea..c3b3014 100644
--- a/drivers/clk/microchip/clk-core.c
+++ b/drivers/clk/microchip/clk-core.c
@@ -199,9 +199,9 @@ static int pbclk_set_rate(struct clk_hw *hw, unsigned long rate,
spin_unlock_irqrestore(&pb->core->reg_lock, flags);
- /* wait again, for pbdivready */
- err = readl_poll_timeout_atomic(pb->ctrl_reg, v, v & PB_DIV_READY,
- 1, LOCK_TIMEOUT_US);
+ /* wait again for DIV_READY */
+ err = readl_poll_timeout(pb->ctrl_reg, v, v & PB_DIV_READY,
+ 1, LOCK_TIMEOUT_US);
if (err)
return err;
diff --git a/drivers/clk/microchip/clk-pic32mzda.c b/drivers/clk/microchip/clk-pic32mzda.c
index 51f5438..9f73477 100644
--- a/drivers/clk/microchip/clk-pic32mzda.c
+++ b/drivers/clk/microchip/clk-pic32mzda.c
@@ -118,6 +118,7 @@ static const struct pic32_sec_osc_data sosc_clk = {
.status_reg = 0x1d0,
.enable_mask = BIT(1),
.status_mask = BIT(4),
+ .fixed_rate = 32768,
.init_data = {
.name = "sosc_clk",
.parent_names = NULL,
diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c
index 383f6a4..0380234 100644
--- a/drivers/clk/mmp/clk-mmp2.c
+++ b/drivers/clk/mmp/clk-mmp2.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/clk/mmp.h>
#include "clk.h"
diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig
index 3165da7..fddc8ac 100644
--- a/drivers/clk/mvebu/Kconfig
+++ b/drivers/clk/mvebu/Kconfig
@@ -24,6 +24,9 @@ config ARMADA_39X_CLK
bool
select MVEBU_CLK_COMMON
+config ARMADA_37XX_CLK
+ bool
+
config ARMADA_XP_CLK
bool
select MVEBU_CLK_COMMON
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
index 7172ef6..d9ae97f 100644
--- a/drivers/clk/mvebu/Makefile
+++ b/drivers/clk/mvebu/Makefile
@@ -6,6 +6,9 @@ obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o
obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o
obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o
obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o
+obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-xtal.o
+obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-tbg.o
+obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-periph.o
obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o
obj-$(CONFIG_ARMADA_CP110_SYSCON) += cp110-system-controller.o
diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
new file mode 100644
index 0000000..45905fc
--- /dev/null
+++ b/drivers/clk/mvebu/armada-37xx-periph.c
@@ -0,0 +1,447 @@
+/*
+ * Marvell Armada 37xx SoC Peripheral clocks
+ *
+ * Copyright (C) 2016 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2 or later. This program is licensed "as is"
+ * without any warranty of any kind, whether express or implied.
+ *
+ * Most of the peripheral clocks can be modelled like this:
+ * _____ _______ _______
+ * TBG-A-P --| | | | | | ______
+ * TBG-B-P --| Mux |--| /div1 |--| /div2 |--| Gate |--> perip_clk
+ * TBG-A-S --| | | | | | |______|
+ * TBG-B-S --|_____| |_______| |_______|
+ *
+ * However some clocks may use only one or two block or and use the
+ * xtal clock as parent.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define TBG_SEL 0x0
+#define DIV_SEL0 0x4
+#define DIV_SEL1 0x8
+#define DIV_SEL2 0xC
+#define CLK_SEL 0x10
+#define CLK_DIS 0x14
+
+struct clk_periph_driver_data {
+ struct clk_hw_onecell_data *hw_data;
+ spinlock_t lock;
+};
+
+struct clk_double_div {
+ struct clk_hw hw;
+ void __iomem *reg1;
+ u8 shift1;
+ void __iomem *reg2;
+ u8 shift2;
+};
+
+#define to_clk_double_div(_hw) container_of(_hw, struct clk_double_div, hw)
+
+struct clk_periph_data {
+ const char *name;
+ const char * const *parent_names;
+ int num_parents;
+ struct clk_hw *mux_hw;
+ struct clk_hw *rate_hw;
+ struct clk_hw *gate_hw;
+ bool is_double_div;
+};
+
+static const struct clk_div_table clk_table6[] = {
+ { .val = 1, .div = 1, },
+ { .val = 2, .div = 2, },
+ { .val = 3, .div = 3, },
+ { .val = 4, .div = 4, },
+ { .val = 5, .div = 5, },
+ { .val = 6, .div = 6, },
+ { .val = 0, .div = 0, }, /* last entry */
+};
+
+static const struct clk_div_table clk_table1[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 0, .div = 0, }, /* last entry */
+};
+
+static const struct clk_div_table clk_table2[] = {
+ { .val = 0, .div = 2, },
+ { .val = 1, .div = 4, },
+ { .val = 0, .div = 0, }, /* last entry */
+};
+static const struct clk_ops clk_double_div_ops;
+
+#define PERIPH_GATE(_name, _bit) \
+struct clk_gate gate_##_name = { \
+ .reg = (void *)CLK_DIS, \
+ .bit_idx = _bit, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_gate_ops, \
+ } \
+};
+
+#define PERIPH_MUX(_name, _shift) \
+struct clk_mux mux_##_name = { \
+ .reg = (void *)TBG_SEL, \
+ .shift = _shift, \
+ .mask = 3, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_mux_ro_ops, \
+ } \
+};
+
+#define PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2) \
+struct clk_double_div rate_##_name = { \
+ .reg1 = (void *)_reg1, \
+ .reg2 = (void *)_reg2, \
+ .shift1 = _shift1, \
+ .shift2 = _shift2, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_double_div_ops, \
+ } \
+};
+
+#define PERIPH_DIV(_name, _reg, _shift, _table) \
+struct clk_divider rate_##_name = { \
+ .reg = (void *)_reg, \
+ .table = _table, \
+ .shift = _shift, \
+ .hw.init = &(struct clk_init_data){ \
+ .ops = &clk_divider_ro_ops, \
+ } \
+};
+
+#define PERIPH_CLK_FULL_DD(_name, _bit, _shift, _reg1, _reg2, _shift1, _shift2)\
+static PERIPH_GATE(_name, _bit); \
+static PERIPH_MUX(_name, _shift); \
+static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2);
+
+#define PERIPH_CLK_FULL(_name, _bit, _shift, _reg, _shift1, _table) \
+static PERIPH_GATE(_name, _bit); \
+static PERIPH_MUX(_name, _shift); \
+static PERIPH_DIV(_name, _reg, _shift1, _table);
+
+#define PERIPH_CLK_GATE_DIV(_name, _bit, _reg, _shift, _table) \
+static PERIPH_GATE(_name, _bit); \
+static PERIPH_DIV(_name, _reg, _shift, _table);
+
+#define PERIPH_CLK_MUX_DIV(_name, _shift, _reg, _shift_div, _table) \
+static PERIPH_MUX(_name, _shift); \
+static PERIPH_DIV(_name, _reg, _shift_div, _table);
+
+#define PERIPH_CLK_MUX_DD(_name, _shift, _reg1, _reg2, _shift1, _shift2)\
+static PERIPH_MUX(_name, _shift); \
+static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2);
+
+#define REF_CLK_FULL(_name) \
+ { .name = #_name, \
+ .parent_names = (const char *[]){ "TBG-A-P", \
+ "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
+ .num_parents = 4, \
+ .mux_hw = &mux_##_name.hw, \
+ .gate_hw = &gate_##_name.hw, \
+ .rate_hw = &rate_##_name.hw, \
+ }
+
+#define REF_CLK_FULL_DD(_name) \
+ { .name = #_name, \
+ .parent_names = (const char *[]){ "TBG-A-P", \
+ "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
+ .num_parents = 4, \
+ .mux_hw = &mux_##_name.hw, \
+ .gate_hw = &gate_##_name.hw, \
+ .rate_hw = &rate_##_name.hw, \
+ .is_double_div = true, \
+ }
+
+#define REF_CLK_GATE(_name, _parent_name) \
+ { .name = #_name, \
+ .parent_names = (const char *[]){ _parent_name}, \
+ .num_parents = 1, \
+ .gate_hw = &gate_##_name.hw, \
+ }
+
+#define REF_CLK_GATE_DIV(_name, _parent_name) \
+ { .name = #_name, \
+ .parent_names = (const char *[]){ _parent_name}, \
+ .num_parents = 1, \
+ .gate_hw = &gate_##_name.hw, \
+ .rate_hw = &rate_##_name.hw, \
+ }
+
+#define REF_CLK_MUX_DIV(_name) \
+ { .name = #_name, \
+ .parent_names = (const char *[]){ "TBG-A-P", \
+ "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
+ .num_parents = 4, \
+ .mux_hw = &mux_##_name.hw, \
+ .rate_hw = &rate_##_name.hw, \
+ }
+
+#define REF_CLK_MUX_DD(_name) \
+ { .name = #_name, \
+ .parent_names = (const char *[]){ "TBG-A-P", \
+ "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
+ .num_parents = 4, \
+ .mux_hw = &mux_##_name.hw, \
+ .rate_hw = &rate_##_name.hw, \
+ .is_double_div = true, \
+ }
+
+/* NB periph clocks */
+PERIPH_CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13);
+PERIPH_CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7);
+PERIPH_CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0);
+PERIPH_CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6);
+PERIPH_CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12);
+PERIPH_CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, clk_table6);
+static PERIPH_GATE(avs, 11);
+PERIPH_CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0);
+PERIPH_CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24);
+static PERIPH_GATE(i2c_2, 16);
+static PERIPH_GATE(i2c_1, 17);
+PERIPH_CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, clk_table2);
+PERIPH_CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12);
+PERIPH_CLK_FULL(trace, 22, 18, DIV_SEL0, 20, clk_table6);
+PERIPH_CLK_FULL(counter, 23, 20, DIV_SEL0, 23, clk_table6);
+PERIPH_CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19);
+PERIPH_CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, clk_table6);
+
+static struct clk_periph_data data_nb[] ={
+ REF_CLK_FULL_DD(mmc),
+ REF_CLK_FULL_DD(sata_host),
+ REF_CLK_FULL_DD(sec_at),
+ REF_CLK_FULL_DD(sec_dap),
+ REF_CLK_FULL_DD(tscem),
+ REF_CLK_FULL(tscem_tmx),
+ REF_CLK_GATE(avs, "xtal"),
+ REF_CLK_FULL_DD(sqf),
+ REF_CLK_FULL_DD(pwm),
+ REF_CLK_GATE(i2c_2, "xtal"),
+ REF_CLK_GATE(i2c_1, "xtal"),
+ REF_CLK_GATE_DIV(ddr_phy, "TBG-A-S"),
+ REF_CLK_FULL_DD(ddr_fclk),
+ REF_CLK_FULL(trace),
+ REF_CLK_FULL(counter),
+ REF_CLK_FULL_DD(eip97),
+ REF_CLK_MUX_DIV(cpu),
+ { },
+};
+
+/* SB periph clocks */
+PERIPH_CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9);
+PERIPH_CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21);
+PERIPH_CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9);
+static PERIPH_GATE(gbe1_50, 0);
+static PERIPH_GATE(gbe0_50, 1);
+static PERIPH_GATE(gbe1_125, 2);
+static PERIPH_GATE(gbe0_125, 3);
+PERIPH_CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, clk_table1);
+PERIPH_CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, clk_table1);
+PERIPH_CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, clk_table1);
+PERIPH_CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6);
+PERIPH_CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12);
+PERIPH_CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18);
+
+static struct clk_periph_data data_sb[] = {
+ REF_CLK_MUX_DD(gbe_50),
+ REF_CLK_MUX_DD(gbe_core),
+ REF_CLK_MUX_DD(gbe_125),
+ REF_CLK_GATE(gbe1_50, "gbe_50"),
+ REF_CLK_GATE(gbe0_50, "gbe_50"),
+ REF_CLK_GATE(gbe1_125, "gbe_125"),
+ REF_CLK_GATE(gbe0_125, "gbe_125"),
+ REF_CLK_GATE_DIV(gbe1_core, "gbe_core"),
+ REF_CLK_GATE_DIV(gbe0_core, "gbe_core"),
+ REF_CLK_GATE_DIV(gbe_bm, "gbe_core"),
+ REF_CLK_FULL_DD(sdio),
+ REF_CLK_FULL_DD(usb32_usb2_sys),
+ REF_CLK_FULL_DD(usb32_ss_sys),
+ { },
+};
+
+static unsigned int get_div(void __iomem *reg, int shift)
+{
+ u32 val;
+
+ val = (readl(reg) >> shift) & 0x7;
+ if (val > 6)
+ return 0;
+ return val;
+}
+
+static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_double_div *double_div = to_clk_double_div(hw);
+ unsigned int div;
+
+ div = get_div(double_div->reg1, double_div->shift1);
+ div *= get_div(double_div->reg2, double_div->shift2);
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate, div);
+}
+
+static const struct clk_ops clk_double_div_ops = {
+ .recalc_rate = clk_double_div_recalc_rate,
+};
+
+static const struct of_device_id armada_3700_periph_clock_of_match[] = {
+ { .compatible = "marvell,armada-3700-periph-clock-nb",
+ .data = data_nb, },
+ { .compatible = "marvell,armada-3700-periph-clock-sb",
+ .data = data_sb, },
+ { }
+};
+static int armada_3700_add_composite_clk(const struct clk_periph_data *data,
+ void __iomem *reg, spinlock_t *lock,
+ struct device *dev, struct clk_hw *hw)
+{
+ const struct clk_ops *mux_ops = NULL, *gate_ops = NULL,
+ *rate_ops = NULL;
+ struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *rate_hw = NULL;
+
+ if (data->mux_hw) {
+ struct clk_mux *mux;
+
+ mux_hw = data->mux_hw;
+ mux = to_clk_mux(mux_hw);
+ mux->lock = lock;
+ mux_ops = mux_hw->init->ops;
+ mux->reg = reg + (u64)mux->reg;
+ }
+
+ if (data->gate_hw) {
+ struct clk_gate *gate;
+
+ gate_hw = data->gate_hw;
+ gate = to_clk_gate(gate_hw);
+ gate->lock = lock;
+ gate_ops = gate_hw->init->ops;
+ gate->reg = reg + (u64)gate->reg;
+ }
+
+ if (data->rate_hw) {
+ rate_hw = data->rate_hw;
+ rate_ops = rate_hw->init->ops;
+ if (data->is_double_div) {
+ struct clk_double_div *rate;
+
+ rate = to_clk_double_div(rate_hw);
+ rate->reg1 = reg + (u64)rate->reg1;
+ rate->reg2 = reg + (u64)rate->reg2;
+ } else {
+ struct clk_divider *rate = to_clk_divider(rate_hw);
+ const struct clk_div_table *clkt;
+ int table_size = 0;
+
+ rate->reg = reg + (u64)rate->reg;
+ for (clkt = rate->table; clkt->div; clkt++)
+ table_size++;
+ rate->width = order_base_2(table_size);
+ rate->lock = lock;
+ }
+ }
+
+ hw = clk_hw_register_composite(dev, data->name, data->parent_names,
+ data->num_parents, mux_hw,
+ mux_ops, rate_hw, rate_ops,
+ gate_hw, gate_ops, CLK_IGNORE_UNUSED);
+
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ return 0;
+}
+
+static int armada_3700_periph_clock_probe(struct platform_device *pdev)
+{
+ struct clk_periph_driver_data *driver_data;
+ struct device_node *np = pdev->dev.of_node;
+ const struct clk_periph_data *data;
+ struct device *dev = &pdev->dev;
+ int num_periph = 0, i, ret;
+ struct resource *res;
+ void __iomem *reg;
+
+ data = of_device_get_match_data(dev);
+ if (!data)
+ return -ENODEV;
+
+ while (data[num_periph].name)
+ num_periph++;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL);
+ if (!driver_data)
+ return -ENOMEM;
+
+ driver_data->hw_data = devm_kzalloc(dev, sizeof(*driver_data->hw_data) +
+ sizeof(*driver_data->hw_data->hws) * num_periph,
+ GFP_KERNEL);
+ if (!driver_data->hw_data)
+ return -ENOMEM;
+ driver_data->hw_data->num = num_periph;
+
+ spin_lock_init(&driver_data->lock);
+
+ for (i = 0; i < num_periph; i++) {
+ struct clk_hw *hw = driver_data->hw_data->hws[i];
+
+ if (armada_3700_add_composite_clk(&data[i], reg,
+ &driver_data->lock, dev, hw))
+ dev_err(dev, "Can't register periph clock %s\n",
+ data[i].name);
+
+ }
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
+ driver_data->hw_data);
+ if (ret) {
+ for (i = 0; i < num_periph; i++)
+ clk_hw_unregister(driver_data->hw_data->hws[i]);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, driver_data);
+ return 0;
+}
+
+static int armada_3700_periph_clock_remove(struct platform_device *pdev)
+{
+ struct clk_periph_driver_data *data = platform_get_drvdata(pdev);
+ struct clk_hw_onecell_data *hw_data = data->hw_data;
+ int i;
+
+ of_clk_del_provider(pdev->dev.of_node);
+
+ for (i = 0; i < hw_data->num; i++)
+ clk_hw_unregister(hw_data->hws[i]);
+
+ return 0;
+}
+
+static struct platform_driver armada_3700_periph_clock_driver = {
+ .probe = armada_3700_periph_clock_probe,
+ .remove = armada_3700_periph_clock_remove,
+ .driver = {
+ .name = "marvell-armada-3700-periph-clock",
+ .of_match_table = armada_3700_periph_clock_of_match,
+ },
+};
+
+builtin_platform_driver(armada_3700_periph_clock_driver);
diff --git a/drivers/clk/mvebu/armada-37xx-tbg.c b/drivers/clk/mvebu/armada-37xx-tbg.c
new file mode 100644
index 0000000..aa80db1
--- /dev/null
+++ b/drivers/clk/mvebu/armada-37xx-tbg.c
@@ -0,0 +1,158 @@
+/*
+ * Marvell Armada 37xx SoC Time Base Generator clocks
+ *
+ * Copyright (C) 2016 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2 or later. This program is licensed "as is"
+ * without any warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define NUM_TBG 4
+
+#define TBG_CTRL0 0x4
+#define TBG_CTRL1 0x8
+#define TBG_CTRL7 0x20
+#define TBG_CTRL8 0x30
+
+#define TBG_DIV_MASK 0x1FF
+
+#define TBG_A_REFDIV 0
+#define TBG_B_REFDIV 16
+
+#define TBG_A_FBDIV 2
+#define TBG_B_FBDIV 18
+
+#define TBG_A_VCODIV_SE 0
+#define TBG_B_VCODIV_SE 16
+
+#define TBG_A_VCODIV_DIFF 1
+#define TBG_B_VCODIV_DIFF 17
+
+struct tbg_def {
+ char *name;
+ u32 refdiv_offset;
+ u32 fbdiv_offset;
+ u32 vcodiv_reg;
+ u32 vcodiv_offset;
+};
+
+static const struct tbg_def tbg[NUM_TBG] = {
+ {"TBG-A-P", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL8, TBG_A_VCODIV_DIFF},
+ {"TBG-B-P", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL8, TBG_B_VCODIV_DIFF},
+ {"TBG-A-S", TBG_A_REFDIV, TBG_A_FBDIV, TBG_CTRL1, TBG_A_VCODIV_SE},
+ {"TBG-B-S", TBG_B_REFDIV, TBG_B_FBDIV, TBG_CTRL1, TBG_B_VCODIV_SE},
+};
+
+static unsigned int tbg_get_mult(void __iomem *reg, const struct tbg_def *ptbg)
+{
+ u32 val;
+
+ val = readl(reg + TBG_CTRL0);
+
+ return ((val >> ptbg->fbdiv_offset) & TBG_DIV_MASK) << 2;
+}
+
+static unsigned int tbg_get_div(void __iomem *reg, const struct tbg_def *ptbg)
+{
+ u32 val;
+ unsigned int div;
+
+ val = readl(reg + TBG_CTRL7);
+
+ div = (val >> ptbg->refdiv_offset) & TBG_DIV_MASK;
+ if (div == 0)
+ div = 1;
+ val = readl(reg + ptbg->vcodiv_reg);
+
+ div *= 1 << ((val >> ptbg->vcodiv_offset) & TBG_DIV_MASK);
+
+ return div;
+}
+
+
+static int armada_3700_tbg_clock_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct clk_hw_onecell_data *hw_tbg_data;
+ struct device *dev = &pdev->dev;
+ const char *parent_name;
+ struct resource *res;
+ struct clk *parent;
+ void __iomem *reg;
+ int i, ret;
+
+ hw_tbg_data = devm_kzalloc(&pdev->dev, sizeof(*hw_tbg_data)
+ + sizeof(*hw_tbg_data->hws) * NUM_TBG,
+ GFP_KERNEL);
+ if (!hw_tbg_data)
+ return -ENOMEM;
+ hw_tbg_data->num = NUM_TBG;
+ platform_set_drvdata(pdev, hw_tbg_data);
+
+ parent = devm_clk_get(dev, NULL);
+ if (IS_ERR(parent)) {
+ dev_err(dev, "Could get the clock parent\n");
+ return -EINVAL;
+ }
+ parent_name = __clk_get_name(parent);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ for (i = 0; i < NUM_TBG; i++) {
+ const char *name;
+ unsigned int mult, div;
+
+ name = tbg[i].name;
+ mult = tbg_get_mult(reg, &tbg[i]);
+ div = tbg_get_div(reg, &tbg[i]);
+ hw_tbg_data->hws[i] = clk_hw_register_fixed_factor(NULL, name,
+ parent_name, 0, mult, div);
+ if (IS_ERR(hw_tbg_data->hws[i]))
+ dev_err(dev, "Can't register TBG clock %s\n", name);
+ }
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, hw_tbg_data);
+
+ return ret;
+}
+
+static int armada_3700_tbg_clock_remove(struct platform_device *pdev)
+{
+ int i;
+ struct clk_hw_onecell_data *hw_tbg_data = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(pdev->dev.of_node);
+ for (i = 0; i < hw_tbg_data->num; i++)
+ clk_hw_unregister_fixed_factor(hw_tbg_data->hws[i]);
+
+ return 0;
+}
+
+static const struct of_device_id armada_3700_tbg_clock_of_match[] = {
+ { .compatible = "marvell,armada-3700-tbg-clock", },
+ { }
+};
+
+static struct platform_driver armada_3700_tbg_clock_driver = {
+ .probe = armada_3700_tbg_clock_probe,
+ .remove = armada_3700_tbg_clock_remove,
+ .driver = {
+ .name = "marvell-armada-3700-tbg-clock",
+ .of_match_table = armada_3700_tbg_clock_of_match,
+ },
+};
+
+builtin_platform_driver(armada_3700_tbg_clock_driver);
diff --git a/drivers/clk/mvebu/armada-37xx-xtal.c b/drivers/clk/mvebu/armada-37xx-xtal.c
new file mode 100644
index 0000000..612d65e
--- /dev/null
+++ b/drivers/clk/mvebu/armada-37xx-xtal.c
@@ -0,0 +1,91 @@
+/*
+ * Marvell Armada 37xx SoC xtal clocks
+ *
+ * Copyright (C) 2016 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define NB_GPIO1_LATCH 0xC
+#define XTAL_MODE BIT(31)
+
+static int armada_3700_xtal_clock_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const char *xtal_name = "xtal";
+ struct device_node *parent;
+ struct regmap *regmap;
+ struct clk_hw *xtal_hw;
+ unsigned int rate;
+ u32 reg;
+ int ret;
+
+ xtal_hw = devm_kzalloc(&pdev->dev, sizeof(*xtal_hw), GFP_KERNEL);
+ if (!xtal_hw)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, xtal_hw);
+
+ parent = np->parent;
+ if (!parent) {
+ dev_err(&pdev->dev, "no parent\n");
+ return -ENODEV;
+ }
+
+ regmap = syscon_node_to_regmap(parent);
+ if (IS_ERR(regmap)) {
+ dev_err(&pdev->dev, "cannot get regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ ret = regmap_read(regmap, NB_GPIO1_LATCH, &reg);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot read from regmap\n");
+ return ret;
+ }
+
+ if (reg & XTAL_MODE)
+ rate = 40000000;
+ else
+ rate = 25000000;
+
+ of_property_read_string_index(np, "clock-output-names", 0, &xtal_name);
+ xtal_hw = clk_hw_register_fixed_rate(NULL, xtal_name, NULL, 0, rate);
+ if (IS_ERR(xtal_hw))
+ return PTR_ERR(xtal_hw);
+ ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, xtal_hw);
+
+ return ret;
+}
+
+static int armada_3700_xtal_clock_remove(struct platform_device *pdev)
+{
+ of_clk_del_provider(pdev->dev.of_node);
+
+ return 0;
+}
+
+static const struct of_device_id armada_3700_xtal_clock_of_match[] = {
+ { .compatible = "marvell,armada-3700-xtal-clock", },
+ { }
+};
+
+static struct platform_driver armada_3700_xtal_clock_driver = {
+ .probe = armada_3700_xtal_clock_probe,
+ .remove = armada_3700_xtal_clock_remove,
+ .driver = {
+ .name = "marvell-armada-3700-xtal-clock",
+ .of_match_table = armada_3700_xtal_clock_of_match,
+ },
+};
+
+builtin_platform_driver(armada_3700_xtal_clock_driver);
diff --git a/drivers/clk/mvebu/armada-39x.c b/drivers/clk/mvebu/armada-39x.c
index efb974d..4fdfd32 100644
--- a/drivers/clk/mvebu/armada-39x.c
+++ b/drivers/clk/mvebu/armada-39x.c
@@ -142,6 +142,8 @@ static const struct clk_gating_soc_desc armada_39x_gating_desc[] __initconst = {
{ "pex3", NULL, 7 },
{ "pex0", NULL, 8 },
{ "usb3h0", NULL, 9 },
+ { "usb3h1", NULL, 10 },
+ { "sata0", NULL, 15 },
{ "sdio", NULL, 17 },
{ "xor0", NULL, 22 },
{ "xor1", NULL, 28 },
diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c
index 7fa42d6..f2303da 100644
--- a/drivers/clk/mvebu/cp110-system-controller.c
+++ b/drivers/clk/mvebu/cp110-system-controller.c
@@ -81,13 +81,6 @@ enum {
#define CP110_GATE_EIP150 25
#define CP110_GATE_EIP197 26
-static struct clk *cp110_clks[CP110_CLK_NUM];
-
-static struct clk_onecell_data cp110_clk_data = {
- .clks = cp110_clks,
- .clk_num = CP110_CLK_NUM,
-};
-
struct cp110_gate_clk {
struct clk_hw hw;
struct regmap *regmap;
@@ -142,6 +135,8 @@ static struct clk *cp110_register_gate(const char *name,
if (!gate)
return ERR_PTR(-ENOMEM);
+ memset(&init, 0, sizeof(init));
+
init.name = name;
init.ops = &cp110_gate_ops;
init.parent_names = &parent_name;
@@ -194,7 +189,8 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
struct regmap *regmap;
struct device_node *np = pdev->dev.of_node;
const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name;
- struct clk *clk;
+ struct clk_onecell_data *cp110_clk_data;
+ struct clk *clk, **cp110_clks;
u32 nand_clk_ctrl;
int i, ret;
@@ -207,6 +203,20 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
if (ret)
return ret;
+ cp110_clks = devm_kcalloc(&pdev->dev, sizeof(struct clk *),
+ CP110_CLK_NUM, GFP_KERNEL);
+ if (!cp110_clks)
+ return -ENOMEM;
+
+ cp110_clk_data = devm_kzalloc(&pdev->dev,
+ sizeof(*cp110_clk_data),
+ GFP_KERNEL);
+ if (!cp110_clk_data)
+ return -ENOMEM;
+
+ cp110_clk_data->clks = cp110_clks;
+ cp110_clk_data->clk_num = CP110_CLK_NUM;
+
/* Register the APLL which is the root of the clk tree */
of_property_read_string_index(np, "core-clock-output-names",
CP110_CORE_APLL, &apll_name);
@@ -334,10 +344,12 @@ static int cp110_syscon_clk_probe(struct platform_device *pdev)
cp110_clks[CP110_MAX_CORE_CLOCKS + i] = clk;
}
- ret = of_clk_add_provider(np, cp110_of_clk_get, &cp110_clk_data);
+ ret = of_clk_add_provider(np, cp110_of_clk_get, cp110_clk_data);
if (ret)
goto fail_clk_add;
+ platform_set_drvdata(pdev, cp110_clks);
+
return 0;
fail_clk_add:
@@ -364,6 +376,7 @@ fail0:
static int cp110_syscon_clk_remove(struct platform_device *pdev)
{
+ struct clk **cp110_clks = platform_get_drvdata(pdev);
int i;
of_clk_del_provider(pdev->dev.of_node);
diff --git a/drivers/clk/nxp/clk-lpc18xx-creg.c b/drivers/clk/nxp/clk-lpc18xx-creg.c
index 9e35749..c6e802e 100644
--- a/drivers/clk/nxp/clk-lpc18xx-creg.c
+++ b/drivers/clk/nxp/clk-lpc18xx-creg.c
@@ -184,7 +184,8 @@ static void __init lpc18xx_creg_clk_init(struct device_node *np)
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_creg_early_data);
}
-CLK_OF_DECLARE(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk", lpc18xx_creg_clk_init);
+CLK_OF_DECLARE_DRIVER(lpc18xx_creg_clk, "nxp,lpc1850-creg-clk",
+ lpc18xx_creg_clk_init);
static struct clk *clk_creg[CREG_CLK_MAX];
static struct clk_onecell_data clk_creg_data = {
diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c
index 90d740a..34c9735 100644
--- a/drivers/clk/nxp/clk-lpc32xx.c
+++ b/drivers/clk/nxp/clk-lpc32xx.c
@@ -1513,6 +1513,7 @@ static void __init lpc32xx_clk_init(struct device_node *np)
if (IS_ERR(clk_regmap)) {
pr_err("failed to regmap system control block: %ld\n",
PTR_ERR(clk_regmap));
+ iounmap(base);
return;
}
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 95e3b3e..0146d3c 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -87,6 +87,23 @@ config MSM_LCC_8960
Say Y if you want to use audio devices such as i2s, pcm,
SLIMBus, etc.
+config MDM_GCC_9615
+ tristate "MDM9615 Global Clock Controller"
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the global clock controller on mdm9615 devices.
+ Say Y if you want to use peripheral devices such as UART, SPI,
+ i2c, USB, SD/eMMC, etc.
+
+config MDM_LCC_9615
+ tristate "MDM9615 LPASS Clock Controller"
+ select MDM_GCC_9615
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the LPASS clock controller on mdm9615 devices.
+ Say Y if you want to use audio devices such as i2s, pcm,
+ SLIMBus, etc.
+
config MSM_MMCC_8960
tristate "MSM8960 Multimedia Clock Controller"
select MSM_GCC_8960
@@ -117,6 +134,7 @@ config MSM_MMCC_8974
config MSM_GCC_8996
tristate "MSM8996 Global Clock Controller"
+ select QCOM_GDSC
depends on COMMON_CLK_QCOM
help
Support for the global clock controller on msm8996 devices.
@@ -126,6 +144,7 @@ config MSM_GCC_8996
config MSM_MMCC_8996
tristate "MSM8996 Multimedia Clock Controller"
select MSM_GCC_8996
+ select QCOM_GDSC
depends on COMMON_CLK_QCOM
help
Support for the multimedia clock controller on msm8996 devices.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 2a25f4e..1fb1f54 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -12,17 +12,20 @@ clk-qcom-y += clk-regmap-mux.o
clk-qcom-y += reset.o
clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
+# Keep alphabetically sorted by config
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o
obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o
obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o
+obj-$(CONFIG_MDM_GCC_9615) += gcc-mdm9615.o
+obj-$(CONFIG_MDM_LCC_9615) += lcc-mdm9615.o
obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
obj-$(CONFIG_MSM_GCC_8916) += gcc-msm8916.o
obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
-obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
obj-$(CONFIG_MSM_GCC_8996) += gcc-msm8996.o
+obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o
diff --git a/drivers/clk/qcom/clk-regmap.c b/drivers/clk/qcom/clk-regmap.c
index a58ba39..1c856d3 100644
--- a/drivers/clk/qcom/clk-regmap.c
+++ b/drivers/clk/qcom/clk-regmap.c
@@ -101,14 +101,13 @@ EXPORT_SYMBOL_GPL(clk_disable_regmap);
* clk_regmap struct via this function so that the regmap is initialized
* and so that the clock is registered with the common clock framework.
*/
-struct clk *devm_clk_register_regmap(struct device *dev,
- struct clk_regmap *rclk)
+int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk)
{
if (dev && dev_get_regmap(dev, NULL))
rclk->regmap = dev_get_regmap(dev, NULL);
else if (dev && dev->parent)
rclk->regmap = dev_get_regmap(dev->parent, NULL);
- return devm_clk_register(dev, &rclk->hw);
+ return devm_clk_hw_register(dev, &rclk->hw);
}
EXPORT_SYMBOL_GPL(devm_clk_register_regmap);
diff --git a/drivers/clk/qcom/clk-regmap.h b/drivers/clk/qcom/clk-regmap.h
index 491a63d..90d95cd 100644
--- a/drivers/clk/qcom/clk-regmap.h
+++ b/drivers/clk/qcom/clk-regmap.h
@@ -39,7 +39,6 @@ struct clk_regmap {
int clk_is_enabled_regmap(struct clk_hw *hw);
int clk_enable_regmap(struct clk_hw *hw);
void clk_disable_regmap(struct clk_hw *hw);
-struct clk *
-devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk);
+int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk);
#endif
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index f7c226a..fffcbaf 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -27,8 +27,8 @@
struct qcom_cc {
struct qcom_reset_controller reset;
- struct clk_onecell_data data;
- struct clk *clks[];
+ struct clk_regmap **rclks;
+ size_t num_rclks;
};
const
@@ -102,8 +102,8 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
struct device_node *clocks_node;
struct clk_fixed_factor *factor;
struct clk_fixed_rate *fixed;
- struct clk *clk;
struct clk_init_data init_data = { };
+ int ret;
clocks_node = of_find_node_by_path("/clocks");
if (clocks_node)
@@ -121,9 +121,9 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
init_data.name = path;
init_data.ops = &clk_fixed_rate_ops;
- clk = devm_clk_register(dev, &fixed->hw);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ ret = devm_clk_hw_register(dev, &fixed->hw);
+ if (ret)
+ return ret;
}
of_node_put(node);
@@ -141,9 +141,9 @@ static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
init_data.flags = 0;
init_data.ops = &clk_fixed_factor_ops;
- clk = devm_clk_register(dev, &factor->hw);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ ret = devm_clk_hw_register(dev, &factor->hw);
+ if (ret)
+ return ret;
}
return 0;
@@ -174,42 +174,48 @@ int qcom_cc_register_sleep_clk(struct device *dev)
}
EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
+static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct qcom_cc *cc = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= cc->num_rclks) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return cc->rclks[idx] ? &cc->rclks[idx]->hw : ERR_PTR(-ENOENT);
+}
+
int qcom_cc_really_probe(struct platform_device *pdev,
const struct qcom_cc_desc *desc, struct regmap *regmap)
{
int i, ret;
struct device *dev = &pdev->dev;
- struct clk *clk;
- struct clk_onecell_data *data;
- struct clk **clks;
struct qcom_reset_controller *reset;
struct qcom_cc *cc;
struct gdsc_desc *scd;
size_t num_clks = desc->num_clks;
struct clk_regmap **rclks = desc->clks;
- cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
- GFP_KERNEL);
+ cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
if (!cc)
return -ENOMEM;
- clks = cc->clks;
- data = &cc->data;
- data->clks = clks;
- data->clk_num = num_clks;
+ cc->rclks = rclks;
+ cc->num_rclks = num_clks;
for (i = 0; i < num_clks; i++) {
- if (!rclks[i]) {
- clks[i] = ERR_PTR(-ENOENT);
+ if (!rclks[i])
continue;
- }
- clk = devm_clk_register_regmap(dev, rclks[i]);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
- clks[i] = clk;
+
+ ret = devm_clk_register_regmap(dev, rclks[i]);
+ if (ret)
+ return ret;
}
- ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
+ ret = of_clk_add_hw_provider(dev->of_node, qcom_cc_clk_hw_get, cc);
if (ret)
return ret;
diff --git a/drivers/clk/qcom/gcc-ipq4019.c b/drivers/clk/qcom/gcc-ipq4019.c
index 3cd1af0..b593065 100644
--- a/drivers/clk/qcom/gcc-ipq4019.c
+++ b/drivers/clk/qcom/gcc-ipq4019.c
@@ -1332,7 +1332,6 @@ static struct platform_driver gcc_ipq4019_driver = {
.probe = gcc_ipq4019_probe,
.driver = {
.name = "qcom,gcc-ipq4019",
- .owner = THIS_MODULE,
.of_match_table = gcc_ipq4019_match_table,
},
};
diff --git a/drivers/clk/qcom/gcc-mdm9615.c b/drivers/clk/qcom/gcc-mdm9615.c
new file mode 100644
index 0000000..581a17f
--- /dev/null
+++ b/drivers/clk/qcom/gcc-mdm9615.c
@@ -0,0 +1,1727 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) BayLibre, SAS.
+ * Author : Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/clock/qcom,gcc-mdm9615.h>
+#include <dt-bindings/reset/qcom,gcc-mdm9615.h>
+
+#include "common.h"
+#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+#include "reset.h"
+
+static struct clk_fixed_factor cxo = {
+ .mult = 1,
+ .div = 1,
+ .hw.init = &(struct clk_init_data){
+ .name = "cxo",
+ .parent_names = (const char *[]){ "cxo_board" },
+ .num_parents = 1,
+ .ops = &clk_fixed_factor_ops,
+ },
+};
+
+static struct clk_pll pll0 = {
+ .l_reg = 0x30c4,
+ .m_reg = 0x30c8,
+ .n_reg = 0x30cc,
+ .config_reg = 0x30d4,
+ .mode_reg = 0x30c0,
+ .status_reg = 0x30d8,
+ .status_bit = 16,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pll0",
+ .parent_names = (const char *[]){ "cxo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap pll0_vote = {
+ .enable_reg = 0x34c0,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "pll0_vote",
+ .parent_names = (const char *[]){ "pll8" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
+static struct clk_regmap pll4_vote = {
+ .enable_reg = 0x34c0,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "pll4_vote",
+ .parent_names = (const char *[]){ "pll4" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
+static struct clk_pll pll8 = {
+ .l_reg = 0x3144,
+ .m_reg = 0x3148,
+ .n_reg = 0x314c,
+ .config_reg = 0x3154,
+ .mode_reg = 0x3140,
+ .status_reg = 0x3158,
+ .status_bit = 16,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pll8",
+ .parent_names = (const char *[]){ "cxo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap pll8_vote = {
+ .enable_reg = 0x34c0,
+ .enable_mask = BIT(8),
+ .hw.init = &(struct clk_init_data){
+ .name = "pll8_vote",
+ .parent_names = (const char *[]){ "pll8" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
+static struct clk_pll pll14 = {
+ .l_reg = 0x31c4,
+ .m_reg = 0x31c8,
+ .n_reg = 0x31cc,
+ .config_reg = 0x31d4,
+ .mode_reg = 0x31c0,
+ .status_reg = 0x31d8,
+ .status_bit = 16,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pll14",
+ .parent_names = (const char *[]){ "cxo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap pll14_vote = {
+ .enable_reg = 0x34c0,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "pll14_vote",
+ .parent_names = (const char *[]){ "pll14" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
+enum {
+ P_CXO,
+ P_PLL8,
+ P_PLL14,
+};
+
+static const struct parent_map gcc_cxo_pll8_map[] = {
+ { P_CXO, 0 },
+ { P_PLL8, 3 }
+};
+
+static const char * const gcc_cxo_pll8[] = {
+ "cxo",
+ "pll8_vote",
+};
+
+static const struct parent_map gcc_cxo_pll14_map[] = {
+ { P_CXO, 0 },
+ { P_PLL14, 4 }
+};
+
+static const char * const gcc_cxo_pll14[] = {
+ "cxo",
+ "pll14_vote",
+};
+
+static const struct parent_map gcc_cxo_map[] = {
+ { P_CXO, 0 },
+};
+
+static const char * const gcc_cxo[] = {
+ "cxo",
+};
+
+static struct freq_tbl clk_tbl_gsbi_uart[] = {
+ { 1843200, P_PLL8, 2, 6, 625 },
+ { 3686400, P_PLL8, 2, 12, 625 },
+ { 7372800, P_PLL8, 2, 24, 625 },
+ { 14745600, P_PLL8, 2, 48, 625 },
+ { 16000000, P_PLL8, 4, 1, 6 },
+ { 24000000, P_PLL8, 4, 1, 4 },
+ { 32000000, P_PLL8, 4, 1, 3 },
+ { 40000000, P_PLL8, 1, 5, 48 },
+ { 46400000, P_PLL8, 1, 29, 240 },
+ { 48000000, P_PLL8, 4, 1, 2 },
+ { 51200000, P_PLL8, 1, 2, 15 },
+ { 56000000, P_PLL8, 1, 7, 48 },
+ { 58982400, P_PLL8, 1, 96, 625 },
+ { 64000000, P_PLL8, 2, 1, 3 },
+ { }
+};
+
+static struct clk_rcg gsbi1_uart_src = {
+ .ns_reg = 0x29d4,
+ .md_reg = 0x29d0,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 16,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_uart,
+ .clkr = {
+ .enable_reg = 0x29d4,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi1_uart_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi1_uart_clk = {
+ .halt_reg = 0x2fcc,
+ .halt_bit = 10,
+ .clkr = {
+ .enable_reg = 0x29d4,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi1_uart_clk",
+ .parent_names = (const char *[]){
+ "gsbi1_uart_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gsbi2_uart_src = {
+ .ns_reg = 0x29f4,
+ .md_reg = 0x29f0,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 16,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_uart,
+ .clkr = {
+ .enable_reg = 0x29f4,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi2_uart_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi2_uart_clk = {
+ .halt_reg = 0x2fcc,
+ .halt_bit = 6,
+ .clkr = {
+ .enable_reg = 0x29f4,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi2_uart_clk",
+ .parent_names = (const char *[]){
+ "gsbi2_uart_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gsbi3_uart_src = {
+ .ns_reg = 0x2a14,
+ .md_reg = 0x2a10,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 16,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_uart,
+ .clkr = {
+ .enable_reg = 0x2a14,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi3_uart_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi3_uart_clk = {
+ .halt_reg = 0x2fcc,
+ .halt_bit = 2,
+ .clkr = {
+ .enable_reg = 0x2a14,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi3_uart_clk",
+ .parent_names = (const char *[]){
+ "gsbi3_uart_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gsbi4_uart_src = {
+ .ns_reg = 0x2a34,
+ .md_reg = 0x2a30,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 16,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_uart,
+ .clkr = {
+ .enable_reg = 0x2a34,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi4_uart_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi4_uart_clk = {
+ .halt_reg = 0x2fd0,
+ .halt_bit = 26,
+ .clkr = {
+ .enable_reg = 0x2a34,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi4_uart_clk",
+ .parent_names = (const char *[]){
+ "gsbi4_uart_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gsbi5_uart_src = {
+ .ns_reg = 0x2a54,
+ .md_reg = 0x2a50,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 16,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_uart,
+ .clkr = {
+ .enable_reg = 0x2a54,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi5_uart_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi5_uart_clk = {
+ .halt_reg = 0x2fd0,
+ .halt_bit = 22,
+ .clkr = {
+ .enable_reg = 0x2a54,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi5_uart_clk",
+ .parent_names = (const char *[]){
+ "gsbi5_uart_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct freq_tbl clk_tbl_gsbi_qup[] = {
+ { 960000, P_CXO, 4, 1, 5 },
+ { 4800000, P_CXO, 4, 0, 1 },
+ { 9600000, P_CXO, 2, 0, 1 },
+ { 15060000, P_PLL8, 1, 2, 51 },
+ { 24000000, P_PLL8, 4, 1, 4 },
+ { 25600000, P_PLL8, 1, 1, 15 },
+ { 48000000, P_PLL8, 4, 1, 2 },
+ { 51200000, P_PLL8, 1, 2, 15 },
+ { }
+};
+
+static struct clk_rcg gsbi1_qup_src = {
+ .ns_reg = 0x29cc,
+ .md_reg = 0x29c8,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_qup,
+ .clkr = {
+ .enable_reg = 0x29cc,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi1_qup_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi1_qup_clk = {
+ .halt_reg = 0x2fcc,
+ .halt_bit = 9,
+ .clkr = {
+ .enable_reg = 0x29cc,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi1_qup_clk",
+ .parent_names = (const char *[]){ "gsbi1_qup_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gsbi2_qup_src = {
+ .ns_reg = 0x29ec,
+ .md_reg = 0x29e8,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_qup,
+ .clkr = {
+ .enable_reg = 0x29ec,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi2_qup_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi2_qup_clk = {
+ .halt_reg = 0x2fcc,
+ .halt_bit = 4,
+ .clkr = {
+ .enable_reg = 0x29ec,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi2_qup_clk",
+ .parent_names = (const char *[]){ "gsbi2_qup_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gsbi3_qup_src = {
+ .ns_reg = 0x2a0c,
+ .md_reg = 0x2a08,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_qup,
+ .clkr = {
+ .enable_reg = 0x2a0c,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi3_qup_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi3_qup_clk = {
+ .halt_reg = 0x2fcc,
+ .halt_bit = 0,
+ .clkr = {
+ .enable_reg = 0x2a0c,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi3_qup_clk",
+ .parent_names = (const char *[]){ "gsbi3_qup_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gsbi4_qup_src = {
+ .ns_reg = 0x2a2c,
+ .md_reg = 0x2a28,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_qup,
+ .clkr = {
+ .enable_reg = 0x2a2c,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi4_qup_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi4_qup_clk = {
+ .halt_reg = 0x2fd0,
+ .halt_bit = 24,
+ .clkr = {
+ .enable_reg = 0x2a2c,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi4_qup_clk",
+ .parent_names = (const char *[]){ "gsbi4_qup_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gsbi5_qup_src = {
+ .ns_reg = 0x2a4c,
+ .md_reg = 0x2a48,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_gsbi_qup,
+ .clkr = {
+ .enable_reg = 0x2a4c,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi5_qup_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ },
+};
+
+static struct clk_branch gsbi5_qup_clk = {
+ .halt_reg = 0x2fd0,
+ .halt_bit = 20,
+ .clkr = {
+ .enable_reg = 0x2a4c,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi5_qup_clk",
+ .parent_names = (const char *[]){ "gsbi5_qup_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static const struct freq_tbl clk_tbl_gp[] = {
+ { 9600000, P_CXO, 2, 0, 0 },
+ { 19200000, P_CXO, 1, 0, 0 },
+ { }
+};
+
+static struct clk_rcg gp0_src = {
+ .ns_reg = 0x2d24,
+ .md_reg = 0x2d00,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_map,
+ },
+ .freq_tbl = clk_tbl_gp,
+ .clkr = {
+ .enable_reg = 0x2d24,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_src",
+ .parent_names = gcc_cxo,
+ .num_parents = 1,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_PARENT_GATE,
+ },
+ }
+};
+
+static struct clk_branch gp0_clk = {
+ .halt_reg = 0x2fd8,
+ .halt_bit = 7,
+ .clkr = {
+ .enable_reg = 0x2d24,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gp0_clk",
+ .parent_names = (const char *[]){ "gp0_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gp1_src = {
+ .ns_reg = 0x2d44,
+ .md_reg = 0x2d40,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_map,
+ },
+ .freq_tbl = clk_tbl_gp,
+ .clkr = {
+ .enable_reg = 0x2d44,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gp1_src",
+ .parent_names = gcc_cxo,
+ .num_parents = 1,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch gp1_clk = {
+ .halt_reg = 0x2fd8,
+ .halt_bit = 6,
+ .clkr = {
+ .enable_reg = 0x2d44,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gp1_clk",
+ .parent_names = (const char *[]){ "gp1_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg gp2_src = {
+ .ns_reg = 0x2d64,
+ .md_reg = 0x2d60,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_map,
+ },
+ .freq_tbl = clk_tbl_gp,
+ .clkr = {
+ .enable_reg = 0x2d64,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "gp2_src",
+ .parent_names = gcc_cxo,
+ .num_parents = 1,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch gp2_clk = {
+ .halt_reg = 0x2fd8,
+ .halt_bit = 5,
+ .clkr = {
+ .enable_reg = 0x2d64,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gp2_clk",
+ .parent_names = (const char *[]){ "gp2_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_branch pmem_clk = {
+ .hwcg_reg = 0x25a0,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fc8,
+ .halt_bit = 20,
+ .clkr = {
+ .enable_reg = 0x25a0,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "pmem_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_rcg prng_src = {
+ .ns_reg = 0x2e80,
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 4,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "prng_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ },
+ },
+};
+
+static struct clk_branch prng_clk = {
+ .halt_reg = 0x2fd8,
+ .halt_check = BRANCH_HALT_VOTED,
+ .halt_bit = 10,
+ .clkr = {
+ .enable_reg = 0x3080,
+ .enable_mask = BIT(10),
+ .hw.init = &(struct clk_init_data){
+ .name = "prng_clk",
+ .parent_names = (const char *[]){ "prng_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static const struct freq_tbl clk_tbl_sdc[] = {
+ { 144000, P_CXO, 1, 1, 133 },
+ { 400000, P_PLL8, 4, 1, 240 },
+ { 16000000, P_PLL8, 4, 1, 6 },
+ { 17070000, P_PLL8, 1, 2, 45 },
+ { 20210000, P_PLL8, 1, 1, 19 },
+ { 24000000, P_PLL8, 4, 1, 4 },
+ { 38400000, P_PLL8, 2, 1, 5 },
+ { 48000000, P_PLL8, 4, 1, 2 },
+ { 64000000, P_PLL8, 3, 1, 2 },
+ { 76800000, P_PLL8, 1, 1, 5 },
+ { }
+};
+
+static struct clk_rcg sdc1_src = {
+ .ns_reg = 0x282c,
+ .md_reg = 0x2828,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_sdc,
+ .clkr = {
+ .enable_reg = 0x282c,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "sdc1_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch sdc1_clk = {
+ .halt_reg = 0x2fc8,
+ .halt_bit = 6,
+ .clkr = {
+ .enable_reg = 0x282c,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "sdc1_clk",
+ .parent_names = (const char *[]){ "sdc1_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg sdc2_src = {
+ .ns_reg = 0x284c,
+ .md_reg = 0x2848,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_sdc,
+ .clkr = {
+ .enable_reg = 0x284c,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "sdc2_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch sdc2_clk = {
+ .halt_reg = 0x2fc8,
+ .halt_bit = 5,
+ .clkr = {
+ .enable_reg = 0x284c,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "sdc2_clk",
+ .parent_names = (const char *[]){ "sdc2_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static const struct freq_tbl clk_tbl_usb[] = {
+ { 60000000, P_PLL8, 1, 5, 32 },
+ { }
+};
+
+static struct clk_rcg usb_hs1_xcvr_src = {
+ .ns_reg = 0x290c,
+ .md_reg = 0x2908,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_usb,
+ .clkr = {
+ .enable_reg = 0x290c,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hs1_xcvr_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch usb_hs1_xcvr_clk = {
+ .halt_reg = 0x2fc8,
+ .halt_bit = 0,
+ .clkr = {
+ .enable_reg = 0x290c,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hs1_xcvr_clk",
+ .parent_names = (const char *[]){ "usb_hs1_xcvr_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg usb_hsic_xcvr_fs_src = {
+ .ns_reg = 0x2928,
+ .md_reg = 0x2924,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_usb,
+ .clkr = {
+ .enable_reg = 0x2928,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hsic_xcvr_fs_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch usb_hsic_xcvr_fs_clk = {
+ .halt_reg = 0x2fc8,
+ .halt_bit = 9,
+ .clkr = {
+ .enable_reg = 0x2928,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hsic_xcvr_fs_clk",
+ .parent_names =
+ (const char *[]){ "usb_hsic_xcvr_fs_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static const struct freq_tbl clk_tbl_usb_hs1_system[] = {
+ { 60000000, P_PLL8, 1, 5, 32 },
+ { }
+};
+
+static struct clk_rcg usb_hs1_system_src = {
+ .ns_reg = 0x36a4,
+ .md_reg = 0x36a0,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_usb_hs1_system,
+ .clkr = {
+ .enable_reg = 0x36a4,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hs1_system_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch usb_hs1_system_clk = {
+ .halt_reg = 0x2fc8,
+ .halt_bit = 4,
+ .clkr = {
+ .enable_reg = 0x36a4,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .parent_names =
+ (const char *[]){ "usb_hs1_system_src" },
+ .num_parents = 1,
+ .name = "usb_hs1_system_clk",
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+ },
+};
+
+static const struct freq_tbl clk_tbl_usb_hsic_system[] = {
+ { 64000000, P_PLL8, 1, 1, 6 },
+ { }
+};
+
+static struct clk_rcg usb_hsic_system_src = {
+ .ns_reg = 0x2b58,
+ .md_reg = 0x2b54,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll8_map,
+ },
+ .freq_tbl = clk_tbl_usb_hsic_system,
+ .clkr = {
+ .enable_reg = 0x2b58,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hsic_system_src",
+ .parent_names = gcc_cxo_pll8,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch usb_hsic_system_clk = {
+ .halt_reg = 0x2fc8,
+ .halt_bit = 7,
+ .clkr = {
+ .enable_reg = 0x2b58,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .parent_names =
+ (const char *[]){ "usb_hsic_system_src" },
+ .num_parents = 1,
+ .name = "usb_hsic_system_clk",
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static const struct freq_tbl clk_tbl_usb_hsic_hsic[] = {
+ { 48000000, P_PLL14, 1, 0, 0 },
+ { }
+};
+
+static struct clk_rcg usb_hsic_hsic_src = {
+ .ns_reg = 0x2b50,
+ .md_reg = 0x2b4c,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = gcc_cxo_pll14_map,
+ },
+ .freq_tbl = clk_tbl_usb_hsic_hsic,
+ .clkr = {
+ .enable_reg = 0x2b50,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hsic_hsic_src",
+ .parent_names = gcc_cxo_pll14,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ }
+};
+
+static struct clk_branch usb_hsic_hsic_clk = {
+ .halt_check = BRANCH_HALT_DELAY,
+ .clkr = {
+ .enable_reg = 0x2b50,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "usb_hsic_hsic_src" },
+ .num_parents = 1,
+ .name = "usb_hsic_hsic_clk",
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_branch usb_hsic_hsio_cal_clk = {
+ .halt_reg = 0x2fc8,
+ .halt_bit = 8,
+ .clkr = {
+ .enable_reg = 0x2b48,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .parent_names = (const char *[]){ "cxo" },
+ .num_parents = 1,
+ .name = "usb_hsic_hsio_cal_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch ce1_core_clk = {
+ .hwcg_reg = 0x2724,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fd4,
+ .halt_bit = 27,
+ .clkr = {
+ .enable_reg = 0x2724,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "ce1_core_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch ce1_h_clk = {
+ .halt_reg = 0x2fd4,
+ .halt_bit = 1,
+ .clkr = {
+ .enable_reg = 0x2720,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "ce1_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch dma_bam_h_clk = {
+ .hwcg_reg = 0x25c0,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fc8,
+ .halt_bit = 12,
+ .clkr = {
+ .enable_reg = 0x25c0,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "dma_bam_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch gsbi1_h_clk = {
+ .hwcg_reg = 0x29c0,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fcc,
+ .halt_bit = 11,
+ .clkr = {
+ .enable_reg = 0x29c0,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi1_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch gsbi2_h_clk = {
+ .hwcg_reg = 0x29e0,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fcc,
+ .halt_bit = 7,
+ .clkr = {
+ .enable_reg = 0x29e0,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi2_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch gsbi3_h_clk = {
+ .hwcg_reg = 0x2a00,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fcc,
+ .halt_bit = 3,
+ .clkr = {
+ .enable_reg = 0x2a00,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi3_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch gsbi4_h_clk = {
+ .hwcg_reg = 0x2a20,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fd0,
+ .halt_bit = 27,
+ .clkr = {
+ .enable_reg = 0x2a20,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi4_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch gsbi5_h_clk = {
+ .hwcg_reg = 0x2a40,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fd0,
+ .halt_bit = 23,
+ .clkr = {
+ .enable_reg = 0x2a40,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "gsbi5_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch usb_hs1_h_clk = {
+ .hwcg_reg = 0x2900,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fc8,
+ .halt_bit = 1,
+ .clkr = {
+ .enable_reg = 0x2900,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hs1_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch usb_hsic_h_clk = {
+ .halt_reg = 0x2fcc,
+ .halt_bit = 28,
+ .clkr = {
+ .enable_reg = 0x2920,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "usb_hsic_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch sdc1_h_clk = {
+ .hwcg_reg = 0x2820,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fc8,
+ .halt_bit = 11,
+ .clkr = {
+ .enable_reg = 0x2820,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "sdc1_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch sdc2_h_clk = {
+ .hwcg_reg = 0x2840,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fc8,
+ .halt_bit = 10,
+ .clkr = {
+ .enable_reg = 0x2840,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "sdc2_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch adm0_clk = {
+ .halt_reg = 0x2fdc,
+ .halt_check = BRANCH_HALT_VOTED,
+ .halt_bit = 14,
+ .clkr = {
+ .enable_reg = 0x3080,
+ .enable_mask = BIT(2),
+ .hw.init = &(struct clk_init_data){
+ .name = "adm0_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch adm0_pbus_clk = {
+ .hwcg_reg = 0x2208,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fdc,
+ .halt_check = BRANCH_HALT_VOTED,
+ .halt_bit = 13,
+ .clkr = {
+ .enable_reg = 0x3080,
+ .enable_mask = BIT(3),
+ .hw.init = &(struct clk_init_data){
+ .name = "adm0_pbus_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch pmic_arb0_h_clk = {
+ .halt_reg = 0x2fd8,
+ .halt_check = BRANCH_HALT_VOTED,
+ .halt_bit = 22,
+ .clkr = {
+ .enable_reg = 0x3080,
+ .enable_mask = BIT(8),
+ .hw.init = &(struct clk_init_data){
+ .name = "pmic_arb0_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch pmic_arb1_h_clk = {
+ .halt_reg = 0x2fd8,
+ .halt_check = BRANCH_HALT_VOTED,
+ .halt_bit = 21,
+ .clkr = {
+ .enable_reg = 0x3080,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "pmic_arb1_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch pmic_ssbi2_clk = {
+ .halt_reg = 0x2fd8,
+ .halt_check = BRANCH_HALT_VOTED,
+ .halt_bit = 23,
+ .clkr = {
+ .enable_reg = 0x3080,
+ .enable_mask = BIT(7),
+ .hw.init = &(struct clk_init_data){
+ .name = "pmic_ssbi2_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_branch rpm_msg_ram_h_clk = {
+ .hwcg_reg = 0x27e0,
+ .hwcg_bit = 6,
+ .halt_reg = 0x2fd8,
+ .halt_check = BRANCH_HALT_VOTED,
+ .halt_bit = 12,
+ .clkr = {
+ .enable_reg = 0x3080,
+ .enable_mask = BIT(6),
+ .hw.init = &(struct clk_init_data){
+ .name = "rpm_msg_ram_h_clk",
+ .ops = &clk_branch_ops,
+ },
+ },
+};
+
+static struct clk_hw *gcc_mdm9615_hws[] = {
+ &cxo.hw,
+};
+
+static struct clk_regmap *gcc_mdm9615_clks[] = {
+ [PLL0] = &pll0.clkr,
+ [PLL0_VOTE] = &pll0_vote,
+ [PLL4_VOTE] = &pll4_vote,
+ [PLL8] = &pll8.clkr,
+ [PLL8_VOTE] = &pll8_vote,
+ [PLL14] = &pll14.clkr,
+ [PLL14_VOTE] = &pll14_vote,
+ [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr,
+ [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr,
+ [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr,
+ [GSBI2_UART_CLK] = &gsbi2_uart_clk.clkr,
+ [GSBI3_UART_SRC] = &gsbi3_uart_src.clkr,
+ [GSBI3_UART_CLK] = &gsbi3_uart_clk.clkr,
+ [GSBI4_UART_SRC] = &gsbi4_uart_src.clkr,
+ [GSBI4_UART_CLK] = &gsbi4_uart_clk.clkr,
+ [GSBI5_UART_SRC] = &gsbi5_uart_src.clkr,
+ [GSBI5_UART_CLK] = &gsbi5_uart_clk.clkr,
+ [GSBI1_QUP_SRC] = &gsbi1_qup_src.clkr,
+ [GSBI1_QUP_CLK] = &gsbi1_qup_clk.clkr,
+ [GSBI2_QUP_SRC] = &gsbi2_qup_src.clkr,
+ [GSBI2_QUP_CLK] = &gsbi2_qup_clk.clkr,
+ [GSBI3_QUP_SRC] = &gsbi3_qup_src.clkr,
+ [GSBI3_QUP_CLK] = &gsbi3_qup_clk.clkr,
+ [GSBI4_QUP_SRC] = &gsbi4_qup_src.clkr,
+ [GSBI4_QUP_CLK] = &gsbi4_qup_clk.clkr,
+ [GSBI5_QUP_SRC] = &gsbi5_qup_src.clkr,
+ [GSBI5_QUP_CLK] = &gsbi5_qup_clk.clkr,
+ [GP0_SRC] = &gp0_src.clkr,
+ [GP0_CLK] = &gp0_clk.clkr,
+ [GP1_SRC] = &gp1_src.clkr,
+ [GP1_CLK] = &gp1_clk.clkr,
+ [GP2_SRC] = &gp2_src.clkr,
+ [GP2_CLK] = &gp2_clk.clkr,
+ [PMEM_A_CLK] = &pmem_clk.clkr,
+ [PRNG_SRC] = &prng_src.clkr,
+ [PRNG_CLK] = &prng_clk.clkr,
+ [SDC1_SRC] = &sdc1_src.clkr,
+ [SDC1_CLK] = &sdc1_clk.clkr,
+ [SDC2_SRC] = &sdc2_src.clkr,
+ [SDC2_CLK] = &sdc2_clk.clkr,
+ [USB_HS1_XCVR_SRC] = &usb_hs1_xcvr_src.clkr,
+ [USB_HS1_XCVR_CLK] = &usb_hs1_xcvr_clk.clkr,
+ [USB_HS1_SYSTEM_CLK_SRC] = &usb_hs1_system_src.clkr,
+ [USB_HS1_SYSTEM_CLK] = &usb_hs1_system_clk.clkr,
+ [USB_HSIC_XCVR_FS_SRC] = &usb_hsic_xcvr_fs_src.clkr,
+ [USB_HSIC_XCVR_FS_CLK] = &usb_hsic_xcvr_fs_clk.clkr,
+ [USB_HSIC_SYSTEM_CLK_SRC] = &usb_hsic_system_src.clkr,
+ [USB_HSIC_SYSTEM_CLK] = &usb_hsic_system_clk.clkr,
+ [USB_HSIC_HSIC_CLK_SRC] = &usb_hsic_hsic_src.clkr,
+ [USB_HSIC_HSIC_CLK] = &usb_hsic_hsic_clk.clkr,
+ [USB_HSIC_HSIO_CAL_CLK] = &usb_hsic_hsio_cal_clk.clkr,
+ [CE1_CORE_CLK] = &ce1_core_clk.clkr,
+ [CE1_H_CLK] = &ce1_h_clk.clkr,
+ [DMA_BAM_H_CLK] = &dma_bam_h_clk.clkr,
+ [GSBI1_H_CLK] = &gsbi1_h_clk.clkr,
+ [GSBI2_H_CLK] = &gsbi2_h_clk.clkr,
+ [GSBI3_H_CLK] = &gsbi3_h_clk.clkr,
+ [GSBI4_H_CLK] = &gsbi4_h_clk.clkr,
+ [GSBI5_H_CLK] = &gsbi5_h_clk.clkr,
+ [USB_HS1_H_CLK] = &usb_hs1_h_clk.clkr,
+ [USB_HSIC_H_CLK] = &usb_hsic_h_clk.clkr,
+ [SDC1_H_CLK] = &sdc1_h_clk.clkr,
+ [SDC2_H_CLK] = &sdc2_h_clk.clkr,
+ [ADM0_CLK] = &adm0_clk.clkr,
+ [ADM0_PBUS_CLK] = &adm0_pbus_clk.clkr,
+ [PMIC_ARB0_H_CLK] = &pmic_arb0_h_clk.clkr,
+ [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr,
+ [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr,
+ [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr,
+};
+
+static const struct qcom_reset_map gcc_mdm9615_resets[] = {
+ [DMA_BAM_RESET] = { 0x25c0, 7 },
+ [CE1_H_RESET] = { 0x2720, 7 },
+ [CE1_CORE_RESET] = { 0x2724, 7 },
+ [SDC1_RESET] = { 0x2830 },
+ [SDC2_RESET] = { 0x2850 },
+ [ADM0_C2_RESET] = { 0x220c, 4 },
+ [ADM0_C1_RESET] = { 0x220c, 3 },
+ [ADM0_C0_RESET] = { 0x220c, 2 },
+ [ADM0_PBUS_RESET] = { 0x220c, 1 },
+ [ADM0_RESET] = { 0x220c },
+ [USB_HS1_RESET] = { 0x2910 },
+ [USB_HSIC_RESET] = { 0x2934 },
+ [GSBI1_RESET] = { 0x29dc },
+ [GSBI2_RESET] = { 0x29fc },
+ [GSBI3_RESET] = { 0x2a1c },
+ [GSBI4_RESET] = { 0x2a3c },
+ [GSBI5_RESET] = { 0x2a5c },
+ [PDM_RESET] = { 0x2CC0, 12 },
+};
+
+static const struct regmap_config gcc_mdm9615_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x3660,
+ .fast_io = true,
+};
+
+static const struct qcom_cc_desc gcc_mdm9615_desc = {
+ .config = &gcc_mdm9615_regmap_config,
+ .clks = gcc_mdm9615_clks,
+ .num_clks = ARRAY_SIZE(gcc_mdm9615_clks),
+ .resets = gcc_mdm9615_resets,
+ .num_resets = ARRAY_SIZE(gcc_mdm9615_resets),
+};
+
+static const struct of_device_id gcc_mdm9615_match_table[] = {
+ { .compatible = "qcom,gcc-mdm9615" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gcc_mdm9615_match_table);
+
+static int gcc_mdm9615_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ int ret;
+ int i;
+
+ regmap = qcom_cc_map(pdev, &gcc_mdm9615_desc);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ for (i = 0; i < ARRAY_SIZE(gcc_mdm9615_hws); i++) {
+ ret = devm_clk_hw_register(dev, gcc_mdm9615_hws[i]);
+ if (ret)
+ return ret;
+ }
+
+ return qcom_cc_really_probe(pdev, &gcc_mdm9615_desc, regmap);
+}
+
+static struct platform_driver gcc_mdm9615_driver = {
+ .probe = gcc_mdm9615_probe,
+ .driver = {
+ .name = "gcc-mdm9615",
+ .of_match_table = gcc_mdm9615_match_table,
+ },
+};
+
+static int __init gcc_mdm9615_init(void)
+{
+ return platform_driver_register(&gcc_mdm9615_driver);
+}
+core_initcall(gcc_mdm9615_init);
+
+static void __exit gcc_mdm9615_exit(void)
+{
+ platform_driver_unregister(&gcc_mdm9615_driver);
+}
+module_exit(gcc_mdm9615_exit);
+
+MODULE_DESCRIPTION("QCOM GCC MDM9615 Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:gcc-mdm9615");
diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c
index bbf732b..fe03e6f 100644
--- a/drivers/clk/qcom/gcc-msm8996.c
+++ b/drivers/clk/qcom/gcc-msm8996.c
@@ -2592,9 +2592,9 @@ static struct clk_branch gcc_pcie_2_aux_clk = {
};
static struct clk_branch gcc_pcie_2_pipe_clk = {
- .halt_reg = 0x6e108,
+ .halt_reg = 0x6e018,
.clkr = {
- .enable_reg = 0x6e108,
+ .enable_reg = 0x6e018,
.enable_mask = BIT(0),
.hw.init = &(struct clk_init_data){
.name = "gcc_pcie_2_pipe_clk",
@@ -3329,6 +3329,8 @@ static const struct qcom_reset_map gcc_msm8996_resets[] = {
[GCC_USB_20_BCR] = { 0x12000 },
[GCC_QUSB2PHY_PRIM_BCR] = { 0x12038 },
[GCC_QUSB2PHY_SEC_BCR] = { 0x1203c },
+ [GCC_USB3_PHY_BCR] = { 0x50020 },
+ [GCC_USB3PHY_PHY_BCR] = { 0x50024 },
[GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 },
[GCC_SDCC1_BCR] = { 0x13000 },
[GCC_SDCC2_BCR] = { 0x14000 },
@@ -3404,6 +3406,8 @@ static const struct qcom_reset_map gcc_msm8996_resets[] = {
[GCC_PCIE_2_BCR] = { 0x6e000 },
[GCC_PCIE_2_PHY_BCR] = { 0x6e038 },
[GCC_PCIE_PHY_BCR] = { 0x6f000 },
+ [GCC_PCIE_PHY_COM_BCR] = { 0x6f014 },
+ [GCC_PCIE_PHY_COM_NOCSR_BCR] = { 0x6f00c },
[GCC_DCD_BCR] = { 0x70000 },
[GCC_OBT_ODT_BCR] = { 0x73000 },
[GCC_UFS_BCR] = { 0x75000 },
@@ -3447,9 +3451,8 @@ MODULE_DEVICE_TABLE(of, gcc_msm8996_match_table);
static int gcc_msm8996_probe(struct platform_device *pdev)
{
- struct clk *clk;
struct device *dev = &pdev->dev;
- int i;
+ int i, ret;
struct regmap *regmap;
regmap = qcom_cc_map(pdev, &gcc_msm8996_desc);
@@ -3463,9 +3466,9 @@ static int gcc_msm8996_probe(struct platform_device *pdev)
regmap_update_bits(regmap, 0x52008, BIT(21), BIT(21));
for (i = 0; i < ARRAY_SIZE(gcc_msm8996_hws); i++) {
- clk = devm_clk_register(dev, gcc_msm8996_hws[i]);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ ret = devm_clk_hw_register(dev, gcc_msm8996_hws[i]);
+ if (ret)
+ return ret;
}
return qcom_cc_really_probe(pdev, &gcc_msm8996_desc, regmap);
diff --git a/drivers/clk/qcom/lcc-mdm9615.c b/drivers/clk/qcom/lcc-mdm9615.c
new file mode 100644
index 0000000..3237ef4
--- /dev/null
+++ b/drivers/clk/qcom/lcc-mdm9615.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) BayLibre, SAS.
+ * Author : Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/clock/qcom,lcc-mdm9615.h>
+
+#include "common.h"
+#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+#include "clk-regmap-divider.h"
+#include "clk-regmap-mux.h"
+
+static struct clk_pll pll4 = {
+ .l_reg = 0x4,
+ .m_reg = 0x8,
+ .n_reg = 0xc,
+ .config_reg = 0x14,
+ .mode_reg = 0x0,
+ .status_reg = 0x18,
+ .status_bit = 16,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pll4",
+ .parent_names = (const char *[]){ "cxo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+enum {
+ P_CXO,
+ P_PLL4,
+};
+
+static const struct parent_map lcc_cxo_pll4_map[] = {
+ { P_CXO, 0 },
+ { P_PLL4, 2 }
+};
+
+static const char * const lcc_cxo_pll4[] = {
+ "cxo",
+ "pll4_vote",
+};
+
+static struct freq_tbl clk_tbl_aif_osr_492[] = {
+ { 512000, P_PLL4, 4, 1, 240 },
+ { 768000, P_PLL4, 4, 1, 160 },
+ { 1024000, P_PLL4, 4, 1, 120 },
+ { 1536000, P_PLL4, 4, 1, 80 },
+ { 2048000, P_PLL4, 4, 1, 60 },
+ { 3072000, P_PLL4, 4, 1, 40 },
+ { 4096000, P_PLL4, 4, 1, 30 },
+ { 6144000, P_PLL4, 4, 1, 20 },
+ { 8192000, P_PLL4, 4, 1, 15 },
+ { 12288000, P_PLL4, 4, 1, 10 },
+ { 24576000, P_PLL4, 4, 1, 5 },
+ { 27000000, P_CXO, 1, 0, 0 },
+ { }
+};
+
+static struct freq_tbl clk_tbl_aif_osr_393[] = {
+ { 512000, P_PLL4, 4, 1, 192 },
+ { 768000, P_PLL4, 4, 1, 128 },
+ { 1024000, P_PLL4, 4, 1, 96 },
+ { 1536000, P_PLL4, 4, 1, 64 },
+ { 2048000, P_PLL4, 4, 1, 48 },
+ { 3072000, P_PLL4, 4, 1, 32 },
+ { 4096000, P_PLL4, 4, 1, 24 },
+ { 6144000, P_PLL4, 4, 1, 16 },
+ { 8192000, P_PLL4, 4, 1, 12 },
+ { 12288000, P_PLL4, 4, 1, 8 },
+ { 24576000, P_PLL4, 4, 1, 4 },
+ { 27000000, P_CXO, 1, 0, 0 },
+ { }
+};
+
+static struct clk_rcg mi2s_osr_src = {
+ .ns_reg = 0x48,
+ .md_reg = 0x4c,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 24,
+ .m_val_shift = 8,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = lcc_cxo_pll4_map,
+ },
+ .freq_tbl = clk_tbl_aif_osr_393,
+ .clkr = {
+ .enable_reg = 0x48,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "mi2s_osr_src",
+ .parent_names = lcc_cxo_pll4,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ },
+};
+
+static const char * const lcc_mi2s_parents[] = {
+ "mi2s_osr_src",
+};
+
+static struct clk_branch mi2s_osr_clk = {
+ .halt_reg = 0x50,
+ .halt_bit = 1,
+ .halt_check = BRANCH_HALT_ENABLE,
+ .clkr = {
+ .enable_reg = 0x48,
+ .enable_mask = BIT(17),
+ .hw.init = &(struct clk_init_data){
+ .name = "mi2s_osr_clk",
+ .parent_names = lcc_mi2s_parents,
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_regmap_div mi2s_div_clk = {
+ .reg = 0x48,
+ .shift = 10,
+ .width = 4,
+ .clkr = {
+ .enable_reg = 0x48,
+ .enable_mask = BIT(15),
+ .hw.init = &(struct clk_init_data){
+ .name = "mi2s_div_clk",
+ .parent_names = lcc_mi2s_parents,
+ .num_parents = 1,
+ .ops = &clk_regmap_div_ops,
+ },
+ },
+};
+
+static struct clk_branch mi2s_bit_div_clk = {
+ .halt_reg = 0x50,
+ .halt_bit = 0,
+ .halt_check = BRANCH_HALT_ENABLE,
+ .clkr = {
+ .enable_reg = 0x48,
+ .enable_mask = BIT(15),
+ .hw.init = &(struct clk_init_data){
+ .name = "mi2s_bit_div_clk",
+ .parent_names = (const char *[]){ "mi2s_div_clk" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_regmap_mux mi2s_bit_clk = {
+ .reg = 0x48,
+ .shift = 14,
+ .width = 1,
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "mi2s_bit_clk",
+ .parent_names = (const char *[]){
+ "mi2s_bit_div_clk",
+ "mi2s_codec_clk",
+ },
+ .num_parents = 2,
+ .ops = &clk_regmap_mux_closest_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+#define CLK_AIF_OSR_DIV(prefix, _ns, _md, hr) \
+static struct clk_rcg prefix##_osr_src = { \
+ .ns_reg = _ns, \
+ .md_reg = _md, \
+ .mn = { \
+ .mnctr_en_bit = 8, \
+ .mnctr_reset_bit = 7, \
+ .mnctr_mode_shift = 5, \
+ .n_val_shift = 24, \
+ .m_val_shift = 8, \
+ .width = 8, \
+ }, \
+ .p = { \
+ .pre_div_shift = 3, \
+ .pre_div_width = 2, \
+ }, \
+ .s = { \
+ .src_sel_shift = 0, \
+ .parent_map = lcc_cxo_pll4_map, \
+ }, \
+ .freq_tbl = clk_tbl_aif_osr_393, \
+ .clkr = { \
+ .enable_reg = _ns, \
+ .enable_mask = BIT(9), \
+ .hw.init = &(struct clk_init_data){ \
+ .name = #prefix "_osr_src", \
+ .parent_names = lcc_cxo_pll4, \
+ .num_parents = 2, \
+ .ops = &clk_rcg_ops, \
+ .flags = CLK_SET_RATE_GATE, \
+ }, \
+ }, \
+}; \
+ \
+static const char * const lcc_##prefix##_parents[] = { \
+ #prefix "_osr_src", \
+}; \
+ \
+static struct clk_branch prefix##_osr_clk = { \
+ .halt_reg = hr, \
+ .halt_bit = 1, \
+ .halt_check = BRANCH_HALT_ENABLE, \
+ .clkr = { \
+ .enable_reg = _ns, \
+ .enable_mask = BIT(21), \
+ .hw.init = &(struct clk_init_data){ \
+ .name = #prefix "_osr_clk", \
+ .parent_names = lcc_##prefix##_parents, \
+ .num_parents = 1, \
+ .ops = &clk_branch_ops, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }, \
+ }, \
+}; \
+ \
+static struct clk_regmap_div prefix##_div_clk = { \
+ .reg = _ns, \
+ .shift = 10, \
+ .width = 8, \
+ .clkr = { \
+ .hw.init = &(struct clk_init_data){ \
+ .name = #prefix "_div_clk", \
+ .parent_names = lcc_##prefix##_parents, \
+ .num_parents = 1, \
+ .ops = &clk_regmap_div_ops, \
+ }, \
+ }, \
+}; \
+ \
+static struct clk_branch prefix##_bit_div_clk = { \
+ .halt_reg = hr, \
+ .halt_bit = 0, \
+ .halt_check = BRANCH_HALT_ENABLE, \
+ .clkr = { \
+ .enable_reg = _ns, \
+ .enable_mask = BIT(19), \
+ .hw.init = &(struct clk_init_data){ \
+ .name = #prefix "_bit_div_clk", \
+ .parent_names = (const char *[]){ \
+ #prefix "_div_clk" \
+ }, \
+ .num_parents = 1, \
+ .ops = &clk_branch_ops, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }, \
+ }, \
+}; \
+ \
+static struct clk_regmap_mux prefix##_bit_clk = { \
+ .reg = _ns, \
+ .shift = 18, \
+ .width = 1, \
+ .clkr = { \
+ .hw.init = &(struct clk_init_data){ \
+ .name = #prefix "_bit_clk", \
+ .parent_names = (const char *[]){ \
+ #prefix "_bit_div_clk", \
+ #prefix "_codec_clk", \
+ }, \
+ .num_parents = 2, \
+ .ops = &clk_regmap_mux_closest_ops, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }, \
+ }, \
+}
+
+CLK_AIF_OSR_DIV(codec_i2s_mic, 0x60, 0x64, 0x68);
+CLK_AIF_OSR_DIV(spare_i2s_mic, 0x78, 0x7c, 0x80);
+CLK_AIF_OSR_DIV(codec_i2s_spkr, 0x6c, 0x70, 0x74);
+CLK_AIF_OSR_DIV(spare_i2s_spkr, 0x84, 0x88, 0x8c);
+
+static struct freq_tbl clk_tbl_pcm_492[] = {
+ { 256000, P_PLL4, 4, 1, 480 },
+ { 512000, P_PLL4, 4, 1, 240 },
+ { 768000, P_PLL4, 4, 1, 160 },
+ { 1024000, P_PLL4, 4, 1, 120 },
+ { 1536000, P_PLL4, 4, 1, 80 },
+ { 2048000, P_PLL4, 4, 1, 60 },
+ { 3072000, P_PLL4, 4, 1, 40 },
+ { 4096000, P_PLL4, 4, 1, 30 },
+ { 6144000, P_PLL4, 4, 1, 20 },
+ { 8192000, P_PLL4, 4, 1, 15 },
+ { 12288000, P_PLL4, 4, 1, 10 },
+ { 24576000, P_PLL4, 4, 1, 5 },
+ { 27000000, P_CXO, 1, 0, 0 },
+ { }
+};
+
+static struct freq_tbl clk_tbl_pcm_393[] = {
+ { 256000, P_PLL4, 4, 1, 384 },
+ { 512000, P_PLL4, 4, 1, 192 },
+ { 768000, P_PLL4, 4, 1, 128 },
+ { 1024000, P_PLL4, 4, 1, 96 },
+ { 1536000, P_PLL4, 4, 1, 64 },
+ { 2048000, P_PLL4, 4, 1, 48 },
+ { 3072000, P_PLL4, 4, 1, 32 },
+ { 4096000, P_PLL4, 4, 1, 24 },
+ { 6144000, P_PLL4, 4, 1, 16 },
+ { 8192000, P_PLL4, 4, 1, 12 },
+ { 12288000, P_PLL4, 4, 1, 8 },
+ { 24576000, P_PLL4, 4, 1, 4 },
+ { 27000000, P_CXO, 1, 0, 0 },
+ { }
+};
+
+static struct clk_rcg pcm_src = {
+ .ns_reg = 0x54,
+ .md_reg = 0x58,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 16,
+ .m_val_shift = 16,
+ .width = 16,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = lcc_cxo_pll4_map,
+ },
+ .freq_tbl = clk_tbl_pcm_393,
+ .clkr = {
+ .enable_reg = 0x54,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "pcm_src",
+ .parent_names = lcc_cxo_pll4,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ },
+};
+
+static struct clk_branch pcm_clk_out = {
+ .halt_reg = 0x5c,
+ .halt_bit = 0,
+ .halt_check = BRANCH_HALT_ENABLE,
+ .clkr = {
+ .enable_reg = 0x54,
+ .enable_mask = BIT(11),
+ .hw.init = &(struct clk_init_data){
+ .name = "pcm_clk_out",
+ .parent_names = (const char *[]){ "pcm_src" },
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_regmap_mux pcm_clk = {
+ .reg = 0x54,
+ .shift = 10,
+ .width = 1,
+ .clkr = {
+ .hw.init = &(struct clk_init_data){
+ .name = "pcm_clk",
+ .parent_names = (const char *[]){
+ "pcm_clk_out",
+ "pcm_codec_clk",
+ },
+ .num_parents = 2,
+ .ops = &clk_regmap_mux_closest_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_rcg slimbus_src = {
+ .ns_reg = 0xcc,
+ .md_reg = 0xd0,
+ .mn = {
+ .mnctr_en_bit = 8,
+ .mnctr_reset_bit = 7,
+ .mnctr_mode_shift = 5,
+ .n_val_shift = 24,
+ .m_val_shift = 8,
+ .width = 8,
+ },
+ .p = {
+ .pre_div_shift = 3,
+ .pre_div_width = 2,
+ },
+ .s = {
+ .src_sel_shift = 0,
+ .parent_map = lcc_cxo_pll4_map,
+ },
+ .freq_tbl = clk_tbl_aif_osr_393,
+ .clkr = {
+ .enable_reg = 0xcc,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "slimbus_src",
+ .parent_names = lcc_cxo_pll4,
+ .num_parents = 2,
+ .ops = &clk_rcg_ops,
+ .flags = CLK_SET_RATE_GATE,
+ },
+ },
+};
+
+static const char * const lcc_slimbus_parents[] = {
+ "slimbus_src",
+};
+
+static struct clk_branch audio_slimbus_clk = {
+ .halt_reg = 0xd4,
+ .halt_bit = 0,
+ .halt_check = BRANCH_HALT_ENABLE,
+ .clkr = {
+ .enable_reg = 0xcc,
+ .enable_mask = BIT(10),
+ .hw.init = &(struct clk_init_data){
+ .name = "audio_slimbus_clk",
+ .parent_names = lcc_slimbus_parents,
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_branch sps_slimbus_clk = {
+ .halt_reg = 0xd4,
+ .halt_bit = 1,
+ .halt_check = BRANCH_HALT_ENABLE,
+ .clkr = {
+ .enable_reg = 0xcc,
+ .enable_mask = BIT(12),
+ .hw.init = &(struct clk_init_data){
+ .name = "sps_slimbus_clk",
+ .parent_names = lcc_slimbus_parents,
+ .num_parents = 1,
+ .ops = &clk_branch_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+ },
+};
+
+static struct clk_regmap *lcc_mdm9615_clks[] = {
+ [PLL4] = &pll4.clkr,
+ [MI2S_OSR_SRC] = &mi2s_osr_src.clkr,
+ [MI2S_OSR_CLK] = &mi2s_osr_clk.clkr,
+ [MI2S_DIV_CLK] = &mi2s_div_clk.clkr,
+ [MI2S_BIT_DIV_CLK] = &mi2s_bit_div_clk.clkr,
+ [MI2S_BIT_CLK] = &mi2s_bit_clk.clkr,
+ [PCM_SRC] = &pcm_src.clkr,
+ [PCM_CLK_OUT] = &pcm_clk_out.clkr,
+ [PCM_CLK] = &pcm_clk.clkr,
+ [SLIMBUS_SRC] = &slimbus_src.clkr,
+ [AUDIO_SLIMBUS_CLK] = &audio_slimbus_clk.clkr,
+ [SPS_SLIMBUS_CLK] = &sps_slimbus_clk.clkr,
+ [CODEC_I2S_MIC_OSR_SRC] = &codec_i2s_mic_osr_src.clkr,
+ [CODEC_I2S_MIC_OSR_CLK] = &codec_i2s_mic_osr_clk.clkr,
+ [CODEC_I2S_MIC_DIV_CLK] = &codec_i2s_mic_div_clk.clkr,
+ [CODEC_I2S_MIC_BIT_DIV_CLK] = &codec_i2s_mic_bit_div_clk.clkr,
+ [CODEC_I2S_MIC_BIT_CLK] = &codec_i2s_mic_bit_clk.clkr,
+ [SPARE_I2S_MIC_OSR_SRC] = &spare_i2s_mic_osr_src.clkr,
+ [SPARE_I2S_MIC_OSR_CLK] = &spare_i2s_mic_osr_clk.clkr,
+ [SPARE_I2S_MIC_DIV_CLK] = &spare_i2s_mic_div_clk.clkr,
+ [SPARE_I2S_MIC_BIT_DIV_CLK] = &spare_i2s_mic_bit_div_clk.clkr,
+ [SPARE_I2S_MIC_BIT_CLK] = &spare_i2s_mic_bit_clk.clkr,
+ [CODEC_I2S_SPKR_OSR_SRC] = &codec_i2s_spkr_osr_src.clkr,
+ [CODEC_I2S_SPKR_OSR_CLK] = &codec_i2s_spkr_osr_clk.clkr,
+ [CODEC_I2S_SPKR_DIV_CLK] = &codec_i2s_spkr_div_clk.clkr,
+ [CODEC_I2S_SPKR_BIT_DIV_CLK] = &codec_i2s_spkr_bit_div_clk.clkr,
+ [CODEC_I2S_SPKR_BIT_CLK] = &codec_i2s_spkr_bit_clk.clkr,
+ [SPARE_I2S_SPKR_OSR_SRC] = &spare_i2s_spkr_osr_src.clkr,
+ [SPARE_I2S_SPKR_OSR_CLK] = &spare_i2s_spkr_osr_clk.clkr,
+ [SPARE_I2S_SPKR_DIV_CLK] = &spare_i2s_spkr_div_clk.clkr,
+ [SPARE_I2S_SPKR_BIT_DIV_CLK] = &spare_i2s_spkr_bit_div_clk.clkr,
+ [SPARE_I2S_SPKR_BIT_CLK] = &spare_i2s_spkr_bit_clk.clkr,
+};
+
+static const struct regmap_config lcc_mdm9615_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xfc,
+ .fast_io = true,
+};
+
+static const struct qcom_cc_desc lcc_mdm9615_desc = {
+ .config = &lcc_mdm9615_regmap_config,
+ .clks = lcc_mdm9615_clks,
+ .num_clks = ARRAY_SIZE(lcc_mdm9615_clks),
+};
+
+static const struct of_device_id lcc_mdm9615_match_table[] = {
+ { .compatible = "qcom,lcc-mdm9615" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lcc_mdm9615_match_table);
+
+static int lcc_mdm9615_probe(struct platform_device *pdev)
+{
+ u32 val;
+ struct regmap *regmap;
+
+ regmap = qcom_cc_map(pdev, &lcc_mdm9615_desc);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ /* Use the correct frequency plan depending on speed of PLL4 */
+ regmap_read(regmap, 0x4, &val);
+ if (val == 0x12) {
+ slimbus_src.freq_tbl = clk_tbl_aif_osr_492;
+ mi2s_osr_src.freq_tbl = clk_tbl_aif_osr_492;
+ codec_i2s_mic_osr_src.freq_tbl = clk_tbl_aif_osr_492;
+ spare_i2s_mic_osr_src.freq_tbl = clk_tbl_aif_osr_492;
+ codec_i2s_spkr_osr_src.freq_tbl = clk_tbl_aif_osr_492;
+ spare_i2s_spkr_osr_src.freq_tbl = clk_tbl_aif_osr_492;
+ pcm_src.freq_tbl = clk_tbl_pcm_492;
+ }
+ /* Enable PLL4 source on the LPASS Primary PLL Mux */
+ regmap_write(regmap, 0xc4, 0x1);
+
+ return qcom_cc_really_probe(pdev, &lcc_mdm9615_desc, regmap);
+}
+
+static struct platform_driver lcc_mdm9615_driver = {
+ .probe = lcc_mdm9615_probe,
+ .driver = {
+ .name = "lcc-mdm9615",
+ .of_match_table = lcc_mdm9615_match_table,
+ },
+};
+module_platform_driver(lcc_mdm9615_driver);
+
+MODULE_DESCRIPTION("QCOM LCC MDM9615 Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lcc-mdm9615");
diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c
index 847dd9d..ca97e11 100644
--- a/drivers/clk/qcom/mmcc-msm8996.c
+++ b/drivers/clk/qcom/mmcc-msm8996.c
@@ -2888,6 +2888,14 @@ static struct clk_hw *mmcc_msm8996_hws[] = {
&gpll0_div.hw,
};
+static struct gdsc mmagic_bimc_gdsc = {
+ .gdscr = 0x529c,
+ .pd = {
+ .name = "mmagic_bimc",
+ },
+ .pwrsts = PWRSTS_OFF_ON,
+};
+
static struct gdsc mmagic_video_gdsc = {
.gdscr = 0x119c,
.gds_hw_ctrl = 0x120c,
@@ -3201,6 +3209,7 @@ static struct clk_regmap *mmcc_msm8996_clocks[] = {
};
static struct gdsc *mmcc_msm8996_gdscs[] = {
+ [MMAGIC_BIMC_GDSC] = &mmagic_bimc_gdsc,
[MMAGIC_VIDEO_GDSC] = &mmagic_video_gdsc,
[MMAGIC_MDSS_GDSC] = &mmagic_mdss_gdsc,
[MMAGIC_CAMSS_GDSC] = &mmagic_camss_gdsc,
@@ -3305,9 +3314,8 @@ MODULE_DEVICE_TABLE(of, mmcc_msm8996_match_table);
static int mmcc_msm8996_probe(struct platform_device *pdev)
{
- struct clk *clk;
struct device *dev = &pdev->dev;
- int i;
+ int i, ret;
struct regmap *regmap;
regmap = qcom_cc_map(pdev, &mmcc_msm8996_desc);
@@ -3320,9 +3328,9 @@ static int mmcc_msm8996_probe(struct platform_device *pdev)
regmap_update_bits(regmap, 0x5054, BIT(15), 0);
for (i = 0; i < ARRAY_SIZE(mmcc_msm8996_hws); i++) {
- clk = devm_clk_register(dev, mmcc_msm8996_hws[i]);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ ret = devm_clk_hw_register(dev, mmcc_msm8996_hws[i]);
+ if (ret)
+ return ret;
}
return qcom_cc_really_probe(pdev, &mmcc_msm8996_desc, regmap);
diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c
index 5093a25..9375777 100644
--- a/drivers/clk/renesas/clk-mstp.c
+++ b/drivers/clk/renesas/clk-mstp.c
@@ -167,7 +167,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np)
unsigned int i;
group = kzalloc(sizeof(*group), GFP_KERNEL);
- clks = kmalloc(MSTP_MAX_CLOCKS * sizeof(*clks), GFP_KERNEL);
+ clks = kmalloc_array(MSTP_MAX_CLOCKS, sizeof(*clks), GFP_KERNEL);
if (group == NULL || clks == NULL) {
kfree(group);
kfree(clks);
diff --git a/drivers/clk/renesas/clk-rz.c b/drivers/clk/renesas/clk-rz.c
index f6312c6..5adb934 100644
--- a/drivers/clk/renesas/clk-rz.c
+++ b/drivers/clk/renesas/clk-rz.c
@@ -25,10 +25,31 @@ struct rz_cpg {
#define CPG_FRQCR 0x10
#define CPG_FRQCR2 0x14
+#define PPR0 0xFCFE3200
+#define PIBC0 0xFCFE7000
+
+#define MD_CLK(x) ((x >> 2) & 1) /* P0_2 */
+
/* -----------------------------------------------------------------------------
* Initialization
*/
+static u16 __init rz_cpg_read_mode_pins(void)
+{
+ void __iomem *ppr0, *pibc0;
+ u16 modes;
+
+ ppr0 = ioremap_nocache(PPR0, 2);
+ pibc0 = ioremap_nocache(PIBC0, 2);
+ BUG_ON(!ppr0 || !pibc0);
+ iowrite16(4, pibc0); /* enable input buffer */
+ modes = ioread16(ppr0);
+ iounmap(ppr0);
+ iounmap(pibc0);
+
+ return modes;
+}
+
static struct clk * __init
rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name)
{
@@ -37,8 +58,7 @@ rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *na
static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 };
if (strcmp(name, "pll") == 0) {
- /* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */
- unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */
+ unsigned int cpg_mode = MD_CLK(rz_cpg_read_mode_pins());
const char *parent_name = of_clk_get_parent_name(np, cpg_mode);
mult = cpg_mode ? (32 / 4) : 30;
diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c
index e38bf60..f255e45 100644
--- a/drivers/clk/renesas/r8a7795-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c
@@ -123,6 +123,10 @@ static const struct mssr_mod_clk r8a7795_mod_clks[] __initconst = {
DEF_MOD("sys-dmac2", 217, R8A7795_CLK_S3D1),
DEF_MOD("sys-dmac1", 218, R8A7795_CLK_S3D1),
DEF_MOD("sys-dmac0", 219, R8A7795_CLK_S3D1),
+ DEF_MOD("cmt3", 300, R8A7795_CLK_R),
+ DEF_MOD("cmt2", 301, R8A7795_CLK_R),
+ DEF_MOD("cmt1", 302, R8A7795_CLK_R),
+ DEF_MOD("cmt0", 303, R8A7795_CLK_R),
DEF_MOD("scif2", 310, R8A7795_CLK_S3D4),
DEF_MOD("sdif3", 311, R8A7795_CLK_SD3),
DEF_MOD("sdif2", 312, R8A7795_CLK_SD2),
diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c
index c84b549..eb347ed 100644
--- a/drivers/clk/renesas/r8a7796-cpg-mssr.c
+++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c
@@ -45,6 +45,7 @@ enum clk_ids {
CLK_S3,
CLK_SDSRC,
CLK_SSPSRC,
+ CLK_RINT,
/* Module Clocks */
MOD_CLK_BASE
@@ -69,6 +70,7 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = {
DEF_FIXED(".s1", CLK_S1, CLK_PLL1_DIV2, 3, 1),
DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1),
DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1),
+ DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1),
/* Core Clock Outputs */
DEF_FIXED("ztr", R8A7796_CLK_ZTR, CLK_PLL1_DIV2, 6, 1),
@@ -92,13 +94,42 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = {
DEF_FIXED("s3d2", R8A7796_CLK_S3D2, CLK_S3, 2, 1),
DEF_FIXED("s3d4", R8A7796_CLK_S3D4, CLK_S3, 4, 1),
+ DEF_GEN3_SD("sd0", R8A7796_CLK_SD0, CLK_SDSRC, 0x0074),
+ DEF_GEN3_SD("sd1", R8A7796_CLK_SD1, CLK_SDSRC, 0x0078),
+ DEF_GEN3_SD("sd2", R8A7796_CLK_SD2, CLK_SDSRC, 0x0268),
+ DEF_GEN3_SD("sd3", R8A7796_CLK_SD3, CLK_SDSRC, 0x026c),
+
DEF_FIXED("cl", R8A7796_CLK_CL, CLK_PLL1_DIV2, 48, 1),
DEF_FIXED("cp", R8A7796_CLK_CP, CLK_EXTAL, 2, 1),
+
+ DEF_DIV6_RO("osc", R8A7796_CLK_OSC, CLK_EXTAL, CPG_RCKCR, 8),
+ DEF_DIV6_RO("r_int", CLK_RINT, CLK_EXTAL, CPG_RCKCR, 32),
+
+ DEF_BASE("r", R8A7796_CLK_R, CLK_TYPE_GEN3_R, CLK_RINT),
};
static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = {
+ DEF_MOD("cmt3", 300, R8A7796_CLK_R),
+ DEF_MOD("cmt2", 301, R8A7796_CLK_R),
+ DEF_MOD("cmt1", 302, R8A7796_CLK_R),
+ DEF_MOD("cmt0", 303, R8A7796_CLK_R),
DEF_MOD("scif2", 310, R8A7796_CLK_S3D4),
+ DEF_MOD("sdif3", 311, R8A7796_CLK_SD3),
+ DEF_MOD("sdif2", 312, R8A7796_CLK_SD2),
+ DEF_MOD("sdif1", 313, R8A7796_CLK_SD1),
+ DEF_MOD("sdif0", 314, R8A7796_CLK_SD0),
+ DEF_MOD("rwdt0", 402, R8A7796_CLK_R),
DEF_MOD("intc-ap", 408, R8A7796_CLK_S3D1),
+ DEF_MOD("thermal", 522, R8A7796_CLK_CP),
+ DEF_MOD("etheravb", 812, R8A7796_CLK_S0D6),
+ DEF_MOD("gpio7", 905, R8A7796_CLK_S3D4),
+ DEF_MOD("gpio6", 906, R8A7796_CLK_S3D4),
+ DEF_MOD("gpio5", 907, R8A7796_CLK_S3D4),
+ DEF_MOD("gpio4", 908, R8A7796_CLK_S3D4),
+ DEF_MOD("gpio3", 909, R8A7796_CLK_S3D4),
+ DEF_MOD("gpio2", 910, R8A7796_CLK_S3D4),
+ DEF_MOD("gpio1", 911, R8A7796_CLK_S3D4),
+ DEF_MOD("gpio0", 912, R8A7796_CLK_S3D4),
};
static const unsigned int r8a7796_crit_mod_clks[] __initconst = {
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index f47a2fa..b5f2c8e 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -8,6 +8,7 @@ obj-y += clk-pll.o
obj-y += clk-cpu.o
obj-y += clk-inverter.o
obj-y += clk-mmc-phase.o
+obj-y += clk-ddr.o
obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
obj-y += clk-rk3036.o
diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c
new file mode 100644
index 0000000..8feba93
--- /dev/null
+++ b/drivers/clk/rockchip/clk-ddr.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
+ * Author: Lin Huang <hl@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <soc/rockchip/rockchip_sip.h>
+#include "clk.h"
+
+struct rockchip_ddrclk {
+ struct clk_hw hw;
+ void __iomem *reg_base;
+ int mux_offset;
+ int mux_shift;
+ int mux_width;
+ int div_shift;
+ int div_width;
+ int ddr_flag;
+ spinlock_t *lock;
+};
+
+#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
+
+static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
+ unsigned long flags;
+ struct arm_smccc_res res;
+
+ spin_lock_irqsave(ddrclk->lock, flags);
+ arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0,
+ ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE,
+ 0, 0, 0, 0, &res);
+ spin_unlock_irqrestore(ddrclk->lock, flags);
+
+ return res.a0;
+}
+
+static unsigned long
+rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
+ ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE,
+ 0, 0, 0, 0, &res);
+
+ return res.a0;
+}
+
+static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0,
+ ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE,
+ 0, 0, 0, 0, &res);
+
+ return res.a0;
+}
+
+static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
+{
+ struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
+ int num_parents = clk_hw_get_num_parents(hw);
+ u32 val;
+
+ val = clk_readl(ddrclk->reg_base +
+ ddrclk->mux_offset) >> ddrclk->mux_shift;
+ val &= GENMASK(ddrclk->mux_width - 1, 0);
+
+ if (val >= num_parents)
+ return -EINVAL;
+
+ return val;
+}
+
+static const struct clk_ops rockchip_ddrclk_sip_ops = {
+ .recalc_rate = rockchip_ddrclk_sip_recalc_rate,
+ .set_rate = rockchip_ddrclk_sip_set_rate,
+ .round_rate = rockchip_ddrclk_sip_round_rate,
+ .get_parent = rockchip_ddrclk_get_parent,
+};
+
+struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
+ const char *const *parent_names,
+ u8 num_parents, int mux_offset,
+ int mux_shift, int mux_width,
+ int div_shift, int div_width,
+ int ddr_flag, void __iomem *reg_base,
+ spinlock_t *lock)
+{
+ struct rockchip_ddrclk *ddrclk;
+ struct clk_init_data init;
+ struct clk *clk;
+
+ ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
+ if (!ddrclk)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ init.flags = flags;
+ init.flags |= CLK_SET_RATE_NO_REPARENT;
+
+ switch (ddr_flag) {
+ case ROCKCHIP_DDRCLK_SIP:
+ init.ops = &rockchip_ddrclk_sip_ops;
+ break;
+ default:
+ pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
+ kfree(ddrclk);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ddrclk->reg_base = reg_base;
+ ddrclk->lock = lock;
+ ddrclk->hw.init = &init;
+ ddrclk->mux_offset = mux_offset;
+ ddrclk->mux_shift = mux_shift;
+ ddrclk->mux_width = mux_width;
+ ddrclk->div_shift = div_shift;
+ ddrclk->div_width = div_width;
+ ddrclk->ddr_flag = ddr_flag;
+
+ clk = clk_register(NULL, &ddrclk->hw);
+ if (IS_ERR(clk)) {
+ pr_err("%s: could not register ddrclk %s\n", __func__, name);
+ kfree(ddrclk);
+ return NULL;
+ }
+
+ return clk;
+}
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index db81e45..9c1373e 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -837,7 +837,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
u8 num_parents, int con_offset, int grf_lock_offset,
int lock_shift, int mode_offset, int mode_shift,
struct rockchip_pll_rate_table *rate_table,
- u8 clk_pll_flags)
+ unsigned long flags, u8 clk_pll_flags)
{
const char *pll_parents[3];
struct clk_init_data init;
@@ -892,7 +892,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
init.name = pll_name;
/* keep all plls untouched for now */
- init.flags = CLK_IGNORE_UNUSED;
+ init.flags = flags | CLK_IGNORE_UNUSED;
init.parent_names = &parent_names[0];
init.num_parents = 1;
diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index cdfabeb..8387c7a 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -100,8 +100,10 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
RK3036_PLL_RATE( 297000000, 1, 99, 4, 2, 1, 0),
RK3036_PLL_RATE( 216000000, 1, 72, 4, 2, 1, 0),
RK3036_PLL_RATE( 148500000, 1, 99, 4, 4, 1, 0),
+ RK3036_PLL_RATE( 106500000, 1, 71, 4, 4, 1, 0),
RK3036_PLL_RATE( 96000000, 1, 64, 4, 4, 1, 0),
RK3036_PLL_RATE( 74250000, 2, 99, 4, 4, 1, 0),
+ RK3036_PLL_RATE( 65000000, 1, 65, 6, 4, 1, 0),
RK3036_PLL_RATE( 54000000, 1, 54, 6, 4, 1, 0),
RK3036_PLL_RATE( 27000000, 1, 27, 6, 4, 1, 0),
{ /* sentinel */ },
@@ -118,6 +120,10 @@ PNAME(mux_armclkb_p) = { "clk_core_b_lpll_src",
"clk_core_b_bpll_src",
"clk_core_b_dpll_src",
"clk_core_b_gpll_src" };
+PNAME(mux_ddrclk_p) = { "clk_ddrc_lpll_src",
+ "clk_ddrc_bpll_src",
+ "clk_ddrc_dpll_src",
+ "clk_ddrc_gpll_src" };
PNAME(mux_aclk_cci_p) = { "cpll_aclk_cci_src",
"gpll_aclk_cci_src",
"npll_aclk_cci_src",
@@ -373,6 +379,7 @@ static struct rockchip_cpuclk_rate_table rk3399_cpuclkb_rates[] __initdata = {
RK3399_CPUCLKB_RATE(2184000000, 1, 11, 11),
RK3399_CPUCLKB_RATE(2088000000, 1, 10, 10),
RK3399_CPUCLKB_RATE(2040000000, 1, 10, 10),
+ RK3399_CPUCLKB_RATE(2016000000, 1, 9, 9),
RK3399_CPUCLKB_RATE(1992000000, 1, 9, 9),
RK3399_CPUCLKB_RATE(1896000000, 1, 9, 9),
RK3399_CPUCLKB_RATE(1800000000, 1, 8, 8),
@@ -578,7 +585,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE(0, "clk_spdif_div", mux_pll_src_cpll_gpll_p, 0,
RK3399_CLKSEL_CON(32), 7, 1, MFLAGS, 0, 7, DFLAGS,
RK3399_CLKGATE_CON(8), 13, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_spdif_frac", "clk_spdif_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_spdif_frac", "clk_spdif_div", 0,
RK3399_CLKSEL_CON(99), 0,
RK3399_CLKGATE_CON(8), 14, GFLAGS,
&rk3399_spdif_fracmux),
@@ -592,7 +599,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE(0, "clk_i2s0_div", mux_pll_src_cpll_gpll_p, 0,
RK3399_CLKSEL_CON(28), 7, 1, MFLAGS, 0, 7, DFLAGS,
RK3399_CLKGATE_CON(8), 3, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_i2s0_frac", "clk_i2s0_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_i2s0_frac", "clk_i2s0_div", 0,
RK3399_CLKSEL_CON(96), 0,
RK3399_CLKGATE_CON(8), 4, GFLAGS,
&rk3399_i2s0_fracmux),
@@ -602,7 +609,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE(0, "clk_i2s1_div", mux_pll_src_cpll_gpll_p, 0,
RK3399_CLKSEL_CON(29), 7, 1, MFLAGS, 0, 7, DFLAGS,
RK3399_CLKGATE_CON(8), 6, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_i2s1_frac", "clk_i2s1_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_i2s1_frac", "clk_i2s1_div", 0,
RK3399_CLKSEL_CON(97), 0,
RK3399_CLKGATE_CON(8), 7, GFLAGS,
&rk3399_i2s1_fracmux),
@@ -612,7 +619,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE(0, "clk_i2s2_div", mux_pll_src_cpll_gpll_p, 0,
RK3399_CLKSEL_CON(30), 7, 1, MFLAGS, 0, 7, DFLAGS,
RK3399_CLKGATE_CON(8), 9, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_i2s2_frac", "clk_i2s2_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_i2s2_frac", "clk_i2s2_div", 0,
RK3399_CLKSEL_CON(98), 0,
RK3399_CLKGATE_CON(8), 10, GFLAGS,
&rk3399_i2s2_fracmux),
@@ -631,7 +638,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE_NOMUX(0, "clk_uart0_div", "clk_uart0_src", 0,
RK3399_CLKSEL_CON(33), 0, 7, DFLAGS,
RK3399_CLKGATE_CON(9), 0, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_uart0_frac", "clk_uart0_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_uart0_frac", "clk_uart0_div", 0,
RK3399_CLKSEL_CON(100), 0,
RK3399_CLKGATE_CON(9), 1, GFLAGS,
&rk3399_uart0_fracmux),
@@ -641,7 +648,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE_NOMUX(0, "clk_uart1_div", "clk_uart_src", 0,
RK3399_CLKSEL_CON(34), 0, 7, DFLAGS,
RK3399_CLKGATE_CON(9), 2, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_uart1_frac", "clk_uart1_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_uart1_frac", "clk_uart1_div", 0,
RK3399_CLKSEL_CON(101), 0,
RK3399_CLKGATE_CON(9), 3, GFLAGS,
&rk3399_uart1_fracmux),
@@ -649,7 +656,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE_NOMUX(0, "clk_uart2_div", "clk_uart_src", 0,
RK3399_CLKSEL_CON(35), 0, 7, DFLAGS,
RK3399_CLKGATE_CON(9), 4, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_uart2_frac", "clk_uart2_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_uart2_frac", "clk_uart2_div", 0,
RK3399_CLKSEL_CON(102), 0,
RK3399_CLKGATE_CON(9), 5, GFLAGS,
&rk3399_uart2_fracmux),
@@ -657,7 +664,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE_NOMUX(0, "clk_uart3_div", "clk_uart_src", 0,
RK3399_CLKSEL_CON(36), 0, 7, DFLAGS,
RK3399_CLKGATE_CON(9), 6, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_uart3_frac", "clk_uart3_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_uart3_frac", "clk_uart3_div", 0,
RK3399_CLKSEL_CON(103), 0,
RK3399_CLKGATE_CON(9), 7, GFLAGS,
&rk3399_uart3_fracmux),
@@ -846,9 +853,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
RK3399_CLKSEL_CON(14), 12, 2, DFLAGS,
RK3399_CLKGATE_CON(5), 4, GFLAGS),
- GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", CLK_IGNORE_UNUSED,
+ GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", 0,
RK3399_CLKGATE_CON(20), 2, GFLAGS),
- GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", CLK_IGNORE_UNUSED,
+ GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", 0,
RK3399_CLKGATE_CON(20), 10, GFLAGS),
GATE(0, "aclk_perihp_noc", "aclk_perihp", CLK_IGNORE_UNUSED,
RK3399_CLKGATE_CON(20), 12, GFLAGS),
@@ -1161,7 +1168,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
RK3399_CLKSEL_CON(49), 8, 2, MFLAGS, 0, 8, DFLAGS,
RK3399_CLKGATE_CON(10), 12, GFLAGS),
- COMPOSITE_FRACMUX_NOGATE(0, "dclk_vop0_frac", "dclk_vop0_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX_NOGATE(DCLK_VOP0_FRAC, "dclk_vop0_frac", "dclk_vop0_div", 0,
RK3399_CLKSEL_CON(106), 0,
&rk3399_dclk_vop0_fracmux),
@@ -1191,7 +1198,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
RK3399_CLKSEL_CON(50), 8, 2, MFLAGS, 0, 8, DFLAGS,
RK3399_CLKGATE_CON(10), 13, GFLAGS),
- COMPOSITE_FRACMUX_NOGATE(0, "dclk_vop1_frac", "dclk_vop1_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX_NOGATE(DCLK_VOP1_FRAC, "dclk_vop1_frac", "dclk_vop1_div", 0,
RK3399_CLKSEL_CON(107), 0,
&rk3399_dclk_vop1_fracmux),
@@ -1305,7 +1312,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
/* testout */
MUX(0, "clk_test_pre", mux_pll_src_cpll_gpll_p, CLK_SET_RATE_PARENT,
RK3399_CLKSEL_CON(58), 7, 1, MFLAGS),
- COMPOSITE_FRAC(0, "clk_test_frac", "clk_test_pre", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRAC(0, "clk_test_frac", "clk_test_pre", 0,
RK3399_CLKSEL_CON(105), 0,
RK3399_CLKGATE_CON(13), 9, GFLAGS),
@@ -1377,6 +1384,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED,
RK3368_CLKSEL_CON(58), 0, 5, DFLAGS,
RK3368_CLKGATE_CON(13), 11, GFLAGS),
+
+ /* ddrc */
+ GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3),
+ 0, GFLAGS),
+ GATE(0, "clk_ddrc_bpll_src", "bpll", 0, RK3399_CLKGATE_CON(3),
+ 1, GFLAGS),
+ GATE(0, "clk_ddrc_dpll_src", "dpll", 0, RK3399_CLKGATE_CON(3),
+ 2, GFLAGS),
+ GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3),
+ 3, GFLAGS),
+ COMPOSITE_DDRCLK(SCLK_DDRC, "sclk_ddrc", mux_ddrclk_p, 0,
+ RK3399_CLKSEL_CON(6), 4, 2, 0, 0, ROCKCHIP_DDRCLK_SIP),
};
static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = {
@@ -1398,7 +1417,7 @@ static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = {
RK3399_PMU_CLKSEL_CON(1), 13, 1, MFLAGS, 8, 5, DFLAGS,
RK3399_PMU_CLKGATE_CON(0), 8, GFLAGS),
- COMPOSITE_FRACMUX_NOGATE(0, "clk_wifi_frac", "clk_wifi_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX_NOGATE(0, "clk_wifi_frac", "clk_wifi_div", 0,
RK3399_PMU_CLKSEL_CON(7), 0,
&rk3399_pmuclk_wifi_fracmux),
@@ -1426,7 +1445,7 @@ static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = {
RK3399_PMU_CLKSEL_CON(5), 10, 1, MFLAGS, 0, 7, DFLAGS,
RK3399_PMU_CLKGATE_CON(0), 5, GFLAGS),
- COMPOSITE_FRACMUX(0, "clk_uart4_frac", "clk_uart4_div", CLK_SET_RATE_PARENT,
+ COMPOSITE_FRACMUX(0, "clk_uart4_frac", "clk_uart4_div", 0,
RK3399_PMU_CLKSEL_CON(6), 0,
RK3399_PMU_CLKGATE_CON(0), 6, GFLAGS,
&rk3399_uart4_pmu_fracmux),
@@ -1468,6 +1487,9 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = {
"aclk_cci_pre",
"aclk_gic",
"aclk_gic_noc",
+ "aclk_hdcp_noc",
+ "hclk_hdcp_noc",
+ "pclk_hdcp_noc",
"pclk_perilp0",
"pclk_perilp0",
"hclk_perilp0",
@@ -1488,6 +1510,10 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = {
"gpll_hclk_perilp1_src",
"gpll_aclk_perilp0_src",
"gpll_aclk_perihp_src",
+ "aclk_vio_noc",
+
+ /* ddrc */
+ "sclk_ddrc"
};
static const char *const rk3399_pmucru_critical_clocks[] __initconst = {
diff --git a/drivers/clk/rockchip/clk-rockchip.c b/drivers/clk/rockchip/clk-rockchip.c
index 4cf838d5..2c9bb81 100644
--- a/drivers/clk/rockchip/clk-rockchip.c
+++ b/drivers/clk/rockchip/clk-rockchip.c
@@ -49,14 +49,19 @@ static void __init rk2928_gate_clk_init(struct device_node *node)
}
reg = of_iomap(node, 0);
+ if (!reg)
+ return;
clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
- if (!clk_data)
+ if (!clk_data) {
+ iounmap(reg);
return;
+ }
clk_data->clks = kzalloc(qty * sizeof(struct clk *), GFP_KERNEL);
if (!clk_data->clks) {
kfree(clk_data);
+ iounmap(reg);
return;
}
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 7ffd134..b886be3 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -385,7 +385,7 @@ void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
list->con_offset, grf_lock_offset,
list->lock_shift, list->mode_offset,
list->mode_shift, list->rate_table,
- list->pll_flags);
+ list->flags, list->pll_flags);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
@@ -484,6 +484,15 @@ void __init rockchip_clk_register_branches(
list->gate_offset, list->gate_shift,
list->gate_flags, flags, &ctx->lock);
break;
+ case branch_ddrclk:
+ clk = rockchip_clk_register_ddrclk(
+ list->name, list->flags,
+ list->parent_names, list->num_parents,
+ list->muxdiv_offset, list->mux_shift,
+ list->mux_width, list->div_shift,
+ list->div_width, list->div_flags,
+ ctx->reg_base, &ctx->lock);
+ break;
}
/* none of the cases above matched */
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 2194ffa..1653edd 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -238,7 +238,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
u8 num_parents, int con_offset, int grf_lock_offset,
int lock_shift, int mode_offset, int mode_shift,
struct rockchip_pll_rate_table *rate_table,
- u8 clk_pll_flags);
+ unsigned long flags, u8 clk_pll_flags);
struct rockchip_cpuclk_clksel {
int reg;
@@ -281,6 +281,20 @@ struct clk *rockchip_clk_register_mmc(const char *name,
const char *const *parent_names, u8 num_parents,
void __iomem *reg, int shift);
+/*
+ * DDRCLK flags, including method of setting the rate
+ * ROCKCHIP_DDRCLK_SIP: use SIP call to bl31 to change ddrclk rate.
+ */
+#define ROCKCHIP_DDRCLK_SIP BIT(0)
+
+struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
+ const char *const *parent_names,
+ u8 num_parents, int mux_offset,
+ int mux_shift, int mux_width,
+ int div_shift, int div_width,
+ int ddr_flags, void __iomem *reg_base,
+ spinlock_t *lock);
+
#define ROCKCHIP_INVERTER_HIWORD_MASK BIT(0)
struct clk *rockchip_clk_register_inverter(const char *name,
@@ -299,6 +313,7 @@ enum rockchip_clk_branch_type {
branch_mmc,
branch_inverter,
branch_factor,
+ branch_ddrclk,
};
struct rockchip_clk_branch {
@@ -488,6 +503,24 @@ struct rockchip_clk_branch {
.child = ch, \
}
+#define COMPOSITE_DDRCLK(_id, cname, pnames, f, mo, ms, mw, \
+ ds, dw, df) \
+ { \
+ .id = _id, \
+ .branch_type = branch_ddrclk, \
+ .name = cname, \
+ .parent_names = pnames, \
+ .num_parents = ARRAY_SIZE(pnames), \
+ .flags = f, \
+ .muxdiv_offset = mo, \
+ .mux_shift = ms, \
+ .mux_width = mw, \
+ .div_shift = ds, \
+ .div_width = dw, \
+ .div_flags = df, \
+ .gate_offset = -1, \
+ }
+
#define MUX(_id, cname, pnames, f, o, s, w, mf) \
{ \
.id = _id, \
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index bdf8b97..51d152f 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -14,18 +14,13 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/syscore_ops.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/exynos-audss-clk.h>
-enum exynos_audss_clk_type {
- TYPE_EXYNOS4210,
- TYPE_EXYNOS5250,
- TYPE_EXYNOS5420,
-};
-
static DEFINE_SPINLOCK(lock);
static struct clk **clk_table;
static void __iomem *reg_base;
@@ -44,9 +39,9 @@ static struct clk *epll;
#ifdef CONFIG_PM_SLEEP
static unsigned long reg_save[][2] = {
- {ASS_CLK_SRC, 0},
- {ASS_CLK_DIV, 0},
- {ASS_CLK_GATE, 0},
+ { ASS_CLK_SRC, 0 },
+ { ASS_CLK_DIV, 0 },
+ { ASS_CLK_GATE, 0 },
};
static int exynos_audss_clk_suspend(void)
@@ -73,14 +68,43 @@ static struct syscore_ops exynos_audss_clk_syscore_ops = {
};
#endif /* CONFIG_PM_SLEEP */
+struct exynos_audss_clk_drvdata {
+ unsigned int has_adma_clk:1;
+ unsigned int has_mst_clk:1;
+ unsigned int enable_epll:1;
+ unsigned int num_clks;
+};
+
+static const struct exynos_audss_clk_drvdata exynos4210_drvdata = {
+ .num_clks = EXYNOS_AUDSS_MAX_CLKS - 1,
+};
+
+static const struct exynos_audss_clk_drvdata exynos5410_drvdata = {
+ .num_clks = EXYNOS_AUDSS_MAX_CLKS - 1,
+ .has_mst_clk = 1,
+};
+
+static const struct exynos_audss_clk_drvdata exynos5420_drvdata = {
+ .num_clks = EXYNOS_AUDSS_MAX_CLKS,
+ .has_adma_clk = 1,
+ .enable_epll = 1,
+};
+
static const struct of_device_id exynos_audss_clk_of_match[] = {
- { .compatible = "samsung,exynos4210-audss-clock",
- .data = (void *)TYPE_EXYNOS4210, },
- { .compatible = "samsung,exynos5250-audss-clock",
- .data = (void *)TYPE_EXYNOS5250, },
- { .compatible = "samsung,exynos5420-audss-clock",
- .data = (void *)TYPE_EXYNOS5420, },
- {},
+ {
+ .compatible = "samsung,exynos4210-audss-clock",
+ .data = &exynos4210_drvdata,
+ }, {
+ .compatible = "samsung,exynos5250-audss-clock",
+ .data = &exynos4210_drvdata,
+ }, {
+ .compatible = "samsung,exynos5410-audss-clock",
+ .data = &exynos5410_drvdata,
+ }, {
+ .compatible = "samsung,exynos5420-audss-clock",
+ .data = &exynos5420_drvdata,
+ },
+ { },
};
static void exynos_audss_clk_teardown(void)
@@ -106,19 +130,17 @@ static void exynos_audss_clk_teardown(void)
/* register exynos_audss clocks */
static int exynos_audss_clk_probe(struct platform_device *pdev)
{
- int i, ret = 0;
- struct resource *res;
const char *mout_audss_p[] = {"fin_pll", "fout_epll"};
const char *mout_i2s_p[] = {"mout_audss", "cdclk0", "sclk_audio0"};
const char *sclk_pcm_p = "sclk_pcm0";
struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
- const struct of_device_id *match;
- enum exynos_audss_clk_type variant;
+ const struct exynos_audss_clk_drvdata *variant;
+ struct resource *res;
+ int i, ret = 0;
- match = of_match_node(exynos_audss_clk_of_match, pdev->dev.of_node);
- if (!match)
+ variant = of_device_get_match_data(&pdev->dev);
+ if (!variant)
return -EINVAL;
- variant = (enum exynos_audss_clk_type)match->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
reg_base = devm_ioremap_resource(&pdev->dev, res);
@@ -126,7 +148,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to map audss registers\n");
return PTR_ERR(reg_base);
}
- /* EPLL don't have to be enabled for boards other than Exynos5420 */
+
epll = ERR_PTR(-ENODEV);
clk_table = devm_kzalloc(&pdev->dev,
@@ -136,10 +158,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
return -ENOMEM;
clk_data.clks = clk_table;
- if (variant == TYPE_EXYNOS5420)
- clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS;
- else
- clk_data.clk_num = EXYNOS_AUDSS_MAX_CLKS - 1;
+ clk_data.clk_num = variant->num_clks;
pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
pll_in = devm_clk_get(&pdev->dev, "pll_in");
@@ -148,13 +167,13 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
if (!IS_ERR(pll_in)) {
mout_audss_p[1] = __clk_get_name(pll_in);
- if (variant == TYPE_EXYNOS5420) {
+ if (variant->enable_epll) {
epll = pll_in;
ret = clk_prepare_enable(epll);
if (ret) {
dev_err(&pdev->dev,
- "failed to prepare the epll clock\n");
+ "failed to prepare the epll clock\n");
return ret;
}
}
@@ -210,7 +229,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
sclk_pcm_p, CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 5, 0, &lock);
- if (variant == TYPE_EXYNOS5420) {
+ if (variant->has_adma_clk) {
clk_table[EXYNOS_ADMA] = clk_register_gate(NULL, "adma",
"dout_srp", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 9, 0, &lock);
@@ -234,9 +253,6 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
register_syscore_ops(&exynos_audss_clk_syscore_ops);
#endif
-
- dev_info(&pdev->dev, "setup completed\n");
-
return 0;
unregister:
diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c
index a43642c..fd1d9bf 100644
--- a/drivers/clk/samsung/clk-exynos5260.c
+++ b/drivers/clk/samsung/clk-exynos5260.c
@@ -131,21 +131,21 @@ static const struct samsung_gate_clock aud_gate_clks[] __initconst = {
EN_IP_AUD, 4, 0, 0),
};
+static const struct samsung_cmu_info aud_cmu __initconst = {
+ .mux_clks = aud_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(aud_mux_clks),
+ .div_clks = aud_div_clks,
+ .nr_div_clks = ARRAY_SIZE(aud_div_clks),
+ .gate_clks = aud_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(aud_gate_clks),
+ .nr_clk_ids = AUD_NR_CLK,
+ .clk_regs = aud_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(aud_clk_regs),
+};
+
static void __init exynos5260_clk_aud_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.mux_clks = aud_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(aud_mux_clks);
- cmu.div_clks = aud_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(aud_div_clks);
- cmu.gate_clks = aud_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(aud_gate_clks);
- cmu.nr_clk_ids = AUD_NR_CLK;
- cmu.clk_regs = aud_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(aud_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &aud_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_aud, "samsung,exynos5260-clock-aud",
@@ -321,21 +321,21 @@ static const struct samsung_gate_clock disp_gate_clks[] __initconst = {
EN_IP_DISP, 25, 0, 0),
};
+static const struct samsung_cmu_info disp_cmu __initconst = {
+ .mux_clks = disp_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(disp_mux_clks),
+ .div_clks = disp_div_clks,
+ .nr_div_clks = ARRAY_SIZE(disp_div_clks),
+ .gate_clks = disp_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(disp_gate_clks),
+ .nr_clk_ids = DISP_NR_CLK,
+ .clk_regs = disp_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(disp_clk_regs),
+};
+
static void __init exynos5260_clk_disp_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.mux_clks = disp_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(disp_mux_clks);
- cmu.div_clks = disp_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(disp_div_clks);
- cmu.gate_clks = disp_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(disp_gate_clks);
- cmu.nr_clk_ids = DISP_NR_CLK;
- cmu.clk_regs = disp_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(disp_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &disp_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_disp, "samsung,exynos5260-clock-disp",
@@ -385,21 +385,21 @@ static const struct samsung_pll_clock egl_pll_clks[] __initconst = {
pll2550_24mhz_tbl),
};
+static const struct samsung_cmu_info egl_cmu __initconst = {
+ .pll_clks = egl_pll_clks,
+ .nr_pll_clks = ARRAY_SIZE(egl_pll_clks),
+ .mux_clks = egl_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(egl_mux_clks),
+ .div_clks = egl_div_clks,
+ .nr_div_clks = ARRAY_SIZE(egl_div_clks),
+ .nr_clk_ids = EGL_NR_CLK,
+ .clk_regs = egl_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(egl_clk_regs),
+};
+
static void __init exynos5260_clk_egl_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.pll_clks = egl_pll_clks;
- cmu.nr_pll_clks = ARRAY_SIZE(egl_pll_clks);
- cmu.mux_clks = egl_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(egl_mux_clks);
- cmu.div_clks = egl_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(egl_div_clks);
- cmu.nr_clk_ids = EGL_NR_CLK;
- cmu.clk_regs = egl_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(egl_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &egl_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_egl, "samsung,exynos5260-clock-egl",
@@ -487,19 +487,19 @@ static const struct samsung_gate_clock fsys_gate_clks[] __initconst = {
EN_IP_FSYS_SECURE_SMMU_RTIC, 12, 0, 0),
};
+static const struct samsung_cmu_info fsys_cmu __initconst = {
+ .mux_clks = fsys_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(fsys_mux_clks),
+ .gate_clks = fsys_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(fsys_gate_clks),
+ .nr_clk_ids = FSYS_NR_CLK,
+ .clk_regs = fsys_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(fsys_clk_regs),
+};
+
static void __init exynos5260_clk_fsys_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.mux_clks = fsys_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(fsys_mux_clks);
- cmu.gate_clks = fsys_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(fsys_gate_clks);
- cmu.nr_clk_ids = FSYS_NR_CLK;
- cmu.clk_regs = fsys_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(fsys_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &fsys_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_fsys, "samsung,exynos5260-clock-fsys",
@@ -576,21 +576,21 @@ static const struct samsung_gate_clock g2d_gate_clks[] __initconst = {
EN_IP_G2D_SECURE_SMMU_G2D, 15, 0, 0),
};
+static const struct samsung_cmu_info g2d_cmu __initconst = {
+ .mux_clks = g2d_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(g2d_mux_clks),
+ .div_clks = g2d_div_clks,
+ .nr_div_clks = ARRAY_SIZE(g2d_div_clks),
+ .gate_clks = g2d_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(g2d_gate_clks),
+ .nr_clk_ids = G2D_NR_CLK,
+ .clk_regs = g2d_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(g2d_clk_regs),
+};
+
static void __init exynos5260_clk_g2d_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.mux_clks = g2d_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(g2d_mux_clks);
- cmu.div_clks = g2d_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(g2d_div_clks);
- cmu.gate_clks = g2d_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(g2d_gate_clks);
- cmu.nr_clk_ids = G2D_NR_CLK;
- cmu.clk_regs = g2d_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(g2d_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &g2d_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_g2d, "samsung,exynos5260-clock-g2d",
@@ -637,23 +637,23 @@ static const struct samsung_pll_clock g3d_pll_clks[] __initconst = {
pll2550_24mhz_tbl),
};
+static const struct samsung_cmu_info g3d_cmu __initconst = {
+ .pll_clks = g3d_pll_clks,
+ .nr_pll_clks = ARRAY_SIZE(g3d_pll_clks),
+ .mux_clks = g3d_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(g3d_mux_clks),
+ .div_clks = g3d_div_clks,
+ .nr_div_clks = ARRAY_SIZE(g3d_div_clks),
+ .gate_clks = g3d_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(g3d_gate_clks),
+ .nr_clk_ids = G3D_NR_CLK,
+ .clk_regs = g3d_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(g3d_clk_regs),
+};
+
static void __init exynos5260_clk_g3d_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.pll_clks = g3d_pll_clks;
- cmu.nr_pll_clks = ARRAY_SIZE(g3d_pll_clks);
- cmu.mux_clks = g3d_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(g3d_mux_clks);
- cmu.div_clks = g3d_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(g3d_div_clks);
- cmu.gate_clks = g3d_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(g3d_gate_clks);
- cmu.nr_clk_ids = G3D_NR_CLK;
- cmu.clk_regs = g3d_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(g3d_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &g3d_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_g3d, "samsung,exynos5260-clock-g3d",
@@ -772,21 +772,21 @@ static const struct samsung_gate_clock gscl_gate_clks[] __initconst = {
EN_IP_GSCL_SECURE_SMMU_MSCL1, 20, 0, 0),
};
+static const struct samsung_cmu_info gscl_cmu __initconst = {
+ .mux_clks = gscl_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(gscl_mux_clks),
+ .div_clks = gscl_div_clks,
+ .nr_div_clks = ARRAY_SIZE(gscl_div_clks),
+ .gate_clks = gscl_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(gscl_gate_clks),
+ .nr_clk_ids = GSCL_NR_CLK,
+ .clk_regs = gscl_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(gscl_clk_regs),
+};
+
static void __init exynos5260_clk_gscl_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.mux_clks = gscl_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(gscl_mux_clks);
- cmu.div_clks = gscl_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(gscl_div_clks);
- cmu.gate_clks = gscl_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(gscl_gate_clks);
- cmu.nr_clk_ids = GSCL_NR_CLK;
- cmu.clk_regs = gscl_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(gscl_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &gscl_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_gscl, "samsung,exynos5260-clock-gscl",
@@ -891,21 +891,21 @@ static const struct samsung_gate_clock isp_gate_clks[] __initconst = {
EN_SCLK_ISP, 9, CLK_SET_RATE_PARENT, 0),
};
+static const struct samsung_cmu_info isp_cmu __initconst = {
+ .mux_clks = isp_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(isp_mux_clks),
+ .div_clks = isp_div_clks,
+ .nr_div_clks = ARRAY_SIZE(isp_div_clks),
+ .gate_clks = isp_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(isp_gate_clks),
+ .nr_clk_ids = ISP_NR_CLK,
+ .clk_regs = isp_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(isp_clk_regs),
+};
+
static void __init exynos5260_clk_isp_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.mux_clks = isp_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(isp_mux_clks);
- cmu.div_clks = isp_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(isp_div_clks);
- cmu.gate_clks = isp_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(isp_gate_clks);
- cmu.nr_clk_ids = ISP_NR_CLK;
- cmu.clk_regs = isp_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(isp_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &isp_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_isp, "samsung,exynos5260-clock-isp",
@@ -955,21 +955,21 @@ static const struct samsung_pll_clock kfc_pll_clks[] __initconst = {
pll2550_24mhz_tbl),
};
+static const struct samsung_cmu_info kfc_cmu __initconst = {
+ .pll_clks = kfc_pll_clks,
+ .nr_pll_clks = ARRAY_SIZE(kfc_pll_clks),
+ .mux_clks = kfc_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(kfc_mux_clks),
+ .div_clks = kfc_div_clks,
+ .nr_div_clks = ARRAY_SIZE(kfc_div_clks),
+ .nr_clk_ids = KFC_NR_CLK,
+ .clk_regs = kfc_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(kfc_clk_regs),
+};
+
static void __init exynos5260_clk_kfc_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.pll_clks = kfc_pll_clks;
- cmu.nr_pll_clks = ARRAY_SIZE(kfc_pll_clks);
- cmu.mux_clks = kfc_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(kfc_mux_clks);
- cmu.div_clks = kfc_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(kfc_div_clks);
- cmu.nr_clk_ids = KFC_NR_CLK;
- cmu.clk_regs = kfc_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(kfc_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &kfc_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_kfc, "samsung,exynos5260-clock-kfc",
@@ -1011,21 +1011,21 @@ static const struct samsung_gate_clock mfc_gate_clks[] __initconst = {
EN_IP_MFC_SECURE_SMMU2_MFC, 7, 0, 0),
};
+static const struct samsung_cmu_info mfc_cmu __initconst = {
+ .mux_clks = mfc_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(mfc_mux_clks),
+ .div_clks = mfc_div_clks,
+ .nr_div_clks = ARRAY_SIZE(mfc_div_clks),
+ .gate_clks = mfc_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(mfc_gate_clks),
+ .nr_clk_ids = MFC_NR_CLK,
+ .clk_regs = mfc_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(mfc_clk_regs),
+};
+
static void __init exynos5260_clk_mfc_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.mux_clks = mfc_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(mfc_mux_clks);
- cmu.div_clks = mfc_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(mfc_div_clks);
- cmu.gate_clks = mfc_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(mfc_gate_clks);
- cmu.nr_clk_ids = MFC_NR_CLK;
- cmu.clk_regs = mfc_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(mfc_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &mfc_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_mfc, "samsung,exynos5260-clock-mfc",
@@ -1158,23 +1158,23 @@ static const struct samsung_pll_clock mif_pll_clks[] __initconst = {
pll2550_24mhz_tbl),
};
+static const struct samsung_cmu_info mif_cmu __initconst = {
+ .pll_clks = mif_pll_clks,
+ .nr_pll_clks = ARRAY_SIZE(mif_pll_clks),
+ .mux_clks = mif_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(mif_mux_clks),
+ .div_clks = mif_div_clks,
+ .nr_div_clks = ARRAY_SIZE(mif_div_clks),
+ .gate_clks = mif_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(mif_gate_clks),
+ .nr_clk_ids = MIF_NR_CLK,
+ .clk_regs = mif_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(mif_clk_regs),
+};
+
static void __init exynos5260_clk_mif_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.pll_clks = mif_pll_clks;
- cmu.nr_pll_clks = ARRAY_SIZE(mif_pll_clks);
- cmu.mux_clks = mif_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(mif_mux_clks);
- cmu.div_clks = mif_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(mif_div_clks);
- cmu.gate_clks = mif_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(mif_gate_clks);
- cmu.nr_clk_ids = MIF_NR_CLK;
- cmu.clk_regs = mif_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(mif_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &mif_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_mif, "samsung,exynos5260-clock-mif",
@@ -1366,21 +1366,21 @@ static const struct samsung_gate_clock peri_gate_clks[] __initconst = {
EN_IP_PERI_SECURE_TZPC, 20, 0, 0),
};
+static const struct samsung_cmu_info peri_cmu __initconst = {
+ .mux_clks = peri_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(peri_mux_clks),
+ .div_clks = peri_div_clks,
+ .nr_div_clks = ARRAY_SIZE(peri_div_clks),
+ .gate_clks = peri_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(peri_gate_clks),
+ .nr_clk_ids = PERI_NR_CLK,
+ .clk_regs = peri_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(peri_clk_regs),
+};
+
static void __init exynos5260_clk_peri_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.mux_clks = peri_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(peri_mux_clks);
- cmu.div_clks = peri_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(peri_div_clks);
- cmu.gate_clks = peri_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(peri_gate_clks);
- cmu.nr_clk_ids = PERI_NR_CLK;
- cmu.clk_regs = peri_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(peri_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &peri_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_peri, "samsung,exynos5260-clock-peri",
@@ -1818,25 +1818,25 @@ static const struct samsung_pll_clock top_pll_clks[] __initconst = {
pll2650_24mhz_tbl),
};
+static const struct samsung_cmu_info top_cmu __initconst = {
+ .pll_clks = top_pll_clks,
+ .nr_pll_clks = ARRAY_SIZE(top_pll_clks),
+ .mux_clks = top_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(top_mux_clks),
+ .div_clks = top_div_clks,
+ .nr_div_clks = ARRAY_SIZE(top_div_clks),
+ .gate_clks = top_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(top_gate_clks),
+ .fixed_clks = fixed_rate_clks,
+ .nr_fixed_clks = ARRAY_SIZE(fixed_rate_clks),
+ .nr_clk_ids = TOP_NR_CLK,
+ .clk_regs = top_clk_regs,
+ .nr_clk_regs = ARRAY_SIZE(top_clk_regs),
+};
+
static void __init exynos5260_clk_top_init(struct device_node *np)
{
- struct samsung_cmu_info cmu = { NULL };
-
- cmu.pll_clks = top_pll_clks;
- cmu.nr_pll_clks = ARRAY_SIZE(top_pll_clks);
- cmu.mux_clks = top_mux_clks;
- cmu.nr_mux_clks = ARRAY_SIZE(top_mux_clks);
- cmu.div_clks = top_div_clks;
- cmu.nr_div_clks = ARRAY_SIZE(top_div_clks);
- cmu.gate_clks = top_gate_clks;
- cmu.nr_gate_clks = ARRAY_SIZE(top_gate_clks);
- cmu.fixed_clks = fixed_rate_clks;
- cmu.nr_fixed_clks = ARRAY_SIZE(fixed_rate_clks);
- cmu.nr_clk_ids = TOP_NR_CLK;
- cmu.clk_regs = top_clk_regs;
- cmu.nr_clk_regs = ARRAY_SIZE(top_clk_regs);
-
- samsung_cmu_register_one(np, &cmu);
+ samsung_cmu_register_one(np, &top_cmu);
}
CLK_OF_DECLARE(exynos5260_clk_top, "samsung,exynos5260-clock-top",
diff --git a/drivers/clk/samsung/clk-exynos5410.c b/drivers/clk/samsung/clk-exynos5410.c
index 54ec486..fc471a4 100644
--- a/drivers/clk/samsung/clk-exynos5410.c
+++ b/drivers/clk/samsung/clk-exynos5410.c
@@ -14,6 +14,7 @@
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/clk.h>
#include "clk.h"
@@ -21,6 +22,8 @@
#define APLL_CON0 0x100
#define CPLL_LOCK 0x10020
#define CPLL_CON0 0x10120
+#define EPLL_LOCK 0x10040
+#define EPLL_CON0 0x10130
#define MPLL_LOCK 0x4000
#define MPLL_CON0 0x4100
#define BPLL_LOCK 0x20010
@@ -58,7 +61,7 @@
/* list of PLLs */
enum exynos5410_plls {
- apll, cpll, mpll,
+ apll, cpll, epll, mpll,
bpll, kpll,
nr_plls /* number of PLLs */
};
@@ -67,6 +70,7 @@ enum exynos5410_plls {
PNAME(apll_p) = { "fin_pll", "fout_apll", };
PNAME(bpll_p) = { "fin_pll", "fout_bpll", };
PNAME(cpll_p) = { "fin_pll", "fout_cpll" };
+PNAME(epll_p) = { "fin_pll", "fout_epll" };
PNAME(mpll_p) = { "fin_pll", "fout_mpll", };
PNAME(kpll_p) = { "fin_pll", "fout_kpll", };
@@ -95,6 +99,8 @@ static const struct samsung_mux_clock exynos5410_mux_clks[] __initconst = {
MUX(0, "sclk_bpll", bpll_p, SRC_CDREX, 0, 1),
MUX(0, "sclk_bpll_muxed", bpll_user_p, SRC_TOP2, 24, 1),
+ MUX(0, "sclk_epll", epll_p, SRC_TOP2, 12, 1),
+
MUX(0, "sclk_cpll", cpll_p, SRC_TOP2, 8, 1),
MUX(0, "sclk_mpll_bpll", mpll_bpll_p, SRC_TOP1, 20, 1),
@@ -176,6 +182,8 @@ static const struct samsung_gate_clock exynos5410_gate_clks[] __initconst = {
GATE(CLK_MMC0, "sdmmc0", "aclk200", GATE_BUS_FSYS0, 12, 0, 0),
GATE(CLK_MMC1, "sdmmc1", "aclk200", GATE_BUS_FSYS0, 13, 0, 0),
GATE(CLK_MMC2, "sdmmc2", "aclk200", GATE_BUS_FSYS0, 14, 0, 0),
+ GATE(CLK_PDMA1, "pdma1", "aclk200", GATE_BUS_FSYS0, 2, 0, 0),
+ GATE(CLK_PDMA0, "pdma0", "aclk200", GATE_BUS_FSYS0, 1, 0, 0),
GATE(CLK_SCLK_USBPHY301, "sclk_usbphy301", "dout_usbphy301",
GATE_TOP_SCLK_FSYS, 7, CLK_SET_RATE_PARENT, 0),
@@ -217,11 +225,26 @@ static const struct samsung_gate_clock exynos5410_gate_clks[] __initconst = {
GATE(CLK_USBD301, "usbd301", "aclk200_fsys", GATE_IP_FSYS, 20, 0, 0),
};
-static const struct samsung_pll_clock exynos5410_plls[nr_plls] __initconst = {
+static const struct samsung_pll_rate_table exynos5410_pll2550x_24mhz_tbl[] __initconst = {
+ PLL_36XX_RATE(400000000U, 200, 3, 2, 0),
+ PLL_36XX_RATE(333000000U, 111, 2, 2, 0),
+ PLL_36XX_RATE(300000000U, 100, 2, 2, 0),
+ PLL_36XX_RATE(266000000U, 266, 3, 3, 0),
+ PLL_36XX_RATE(200000000U, 200, 3, 3, 0),
+ PLL_36XX_RATE(192000000U, 192, 3, 3, 0),
+ PLL_36XX_RATE(166000000U, 166, 3, 3, 0),
+ PLL_36XX_RATE(133000000U, 266, 3, 4, 0),
+ PLL_36XX_RATE(100000000U, 200, 3, 4, 0),
+ PLL_36XX_RATE(66000000U, 176, 2, 5, 0),
+};
+
+static struct samsung_pll_clock exynos5410_plls[nr_plls] __initdata = {
[apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK,
APLL_CON0, NULL),
[cpll] = PLL(pll_35xx, CLK_FOUT_CPLL, "fout_cpll", "fin_pll", CPLL_LOCK,
CPLL_CON0, NULL),
+ [epll] = PLL(pll_2650x, CLK_FOUT_EPLL, "fout_epll", "fin_pll", EPLL_LOCK,
+ EPLL_CON0, NULL),
[mpll] = PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", MPLL_LOCK,
MPLL_CON0, NULL),
[bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", BPLL_LOCK,
@@ -230,29 +253,27 @@ static const struct samsung_pll_clock exynos5410_plls[nr_plls] __initconst = {
KPLL_CON0, NULL),
};
+static const struct samsung_cmu_info cmu __initconst = {
+ .pll_clks = exynos5410_plls,
+ .nr_pll_clks = ARRAY_SIZE(exynos5410_plls),
+ .mux_clks = exynos5410_mux_clks,
+ .nr_mux_clks = ARRAY_SIZE(exynos5410_mux_clks),
+ .div_clks = exynos5410_div_clks,
+ .nr_div_clks = ARRAY_SIZE(exynos5410_div_clks),
+ .gate_clks = exynos5410_gate_clks,
+ .nr_gate_clks = ARRAY_SIZE(exynos5410_gate_clks),
+ .nr_clk_ids = CLK_NR_CLKS,
+};
+
/* register exynos5410 clocks */
static void __init exynos5410_clk_init(struct device_node *np)
{
- struct samsung_clk_provider *ctx;
- void __iomem *reg_base;
-
- reg_base = of_iomap(np, 0);
- if (!reg_base)
- panic("%s: failed to map registers\n", __func__);
-
- ctx = samsung_clk_init(np, reg_base, CLK_NR_CLKS);
-
- samsung_clk_register_pll(ctx, exynos5410_plls,
- ARRAY_SIZE(exynos5410_plls), reg_base);
+ struct clk *xxti = of_clk_get(np, 0);
- samsung_clk_register_mux(ctx, exynos5410_mux_clks,
- ARRAY_SIZE(exynos5410_mux_clks));
- samsung_clk_register_div(ctx, exynos5410_div_clks,
- ARRAY_SIZE(exynos5410_div_clks));
- samsung_clk_register_gate(ctx, exynos5410_gate_clks,
- ARRAY_SIZE(exynos5410_gate_clks));
+ if (!IS_ERR(xxti) && clk_get_rate(xxti) == 24 * MHZ)
+ exynos5410_plls[epll].rate_table = exynos5410_pll2550x_24mhz_tbl;
- samsung_clk_of_add_provider(np, ctx);
+ samsung_cmu_register_one(np, &cmu);
pr_debug("Exynos5410: clock setup completed.\n");
}
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index bb196ca..8c8b495 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -131,6 +131,9 @@
#define TOP_SPARE2 0x10b08
#define BPLL_LOCK 0x20010
#define BPLL_CON0 0x20110
+#define SRC_CDREX 0x20200
+#define DIV_CDREX0 0x20500
+#define DIV_CDREX1 0x20504
#define KPLL_LOCK 0x28000
#define KPLL_CON0 0x28100
#define SRC_KFC 0x28200
@@ -244,6 +247,9 @@ static const unsigned long exynos5x_clk_regs[] __initconst = {
GATE_TOP_SCLK_FSYS,
GATE_TOP_SCLK_PERIC,
TOP_SPARE2,
+ SRC_CDREX,
+ DIV_CDREX0,
+ DIV_CDREX1,
SRC_KFC,
DIV_KFC0,
};
@@ -448,6 +454,8 @@ PNAME(mout_maudio0_p) = {"fin_pll", "maudio_clk", "mout_sclk_dpll",
"mout_sclk_epll", "mout_sclk_rpll"};
PNAME(mout_mau_epll_clk_p) = {"mout_sclk_epll", "mout_sclk_dpll",
"mout_sclk_mpll", "mout_sclk_spll"};
+PNAME(mout_mclk_cdrex_p) = {"mout_bpll", "mout_mx_mspll_ccore"};
+
/* List of parents specific to exynos5800 */
PNAME(mout_epll2_5800_p) = { "mout_sclk_epll", "ff_dout_epll2" };
PNAME(mout_group1_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll",
@@ -465,6 +473,9 @@ PNAME(mout_group6_5800_p) = { "mout_sclk_ipll", "mout_sclk_dpll",
PNAME(mout_group7_5800_p) = { "mout_sclk_cpll", "mout_sclk_dpll",
"mout_sclk_mpll", "mout_sclk_spll",
"mout_epll2", "mout_sclk_ipll" };
+PNAME(mout_mx_mspll_ccore_p) = {"sclk_bpll", "mout_sclk_dpll",
+ "mout_sclk_mpll", "ff_dout_spll2",
+ "mout_sclk_spll", "mout_sclk_epll"};
PNAME(mout_mau_epll_clk_5800_p) = { "mout_sclk_epll", "mout_sclk_dpll",
"mout_sclk_mpll",
"ff_dout_spll2" };
@@ -523,6 +534,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = {
MUX(0, "mout_aclk300_disp1", mout_group5_5800_p, SRC_TOP2, 24, 2),
MUX(0, "mout_aclk300_gscl", mout_group5_5800_p, SRC_TOP2, 28, 2),
+ MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore",
+ mout_mx_mspll_ccore_p, SRC_TOP7, 16, 2),
MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, SRC_TOP7,
20, 2),
MUX(0, "sclk_bpll", mout_bpll_p, SRC_TOP7, 24, 1),
@@ -601,6 +614,8 @@ static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = {
MUX(0, "mout_aclk300_disp1", mout_group1_p, SRC_TOP2, 24, 2),
MUX(0, "mout_aclk300_gscl", mout_group1_p, SRC_TOP2, 28, 2),
+ MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore",
+ mout_group5_5800_p, SRC_TOP7, 16, 2),
MUX(0, "mout_mau_epll_clk", mout_mau_epll_clk_p, SRC_TOP7, 20, 2),
MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1),
@@ -744,6 +759,12 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = {
MUX(0, "mout_fimd1_final", mout_fimd1_final_p, TOP_SPARE2, 8, 1),
+ /* CDREX block */
+ MUX_F(CLK_MOUT_MCLK_CDREX, "mout_mclk_cdrex", mout_mclk_cdrex_p,
+ SRC_CDREX, 4, 1, CLK_SET_RATE_PARENT, 0),
+ MUX_F(CLK_MOUT_BPLL, "mout_bpll", mout_bpll_p, SRC_CDREX, 0, 1,
+ CLK_SET_RATE_PARENT, 0),
+
/* MAU Block */
MUX(CLK_MOUT_MAUDIO0, "mout_maudio0", mout_maudio0_p, SRC_MAU, 28, 3),
@@ -836,6 +857,21 @@ static const struct samsung_div_clock exynos5x_div_clks[] __initconst = {
DIV(CLK_DOUT_ACLK400_DISP1, "dout_aclk400_disp1",
"mout_aclk400_disp1", DIV_TOP2, 4, 3),
+ /* CDREX Block */
+ DIV(CLK_DOUT_PCLK_CDREX, "dout_pclk_cdrex", "dout_aclk_cdrex1",
+ DIV_CDREX0, 28, 3),
+ DIV_F(CLK_DOUT_SCLK_CDREX, "dout_sclk_cdrex", "mout_mclk_cdrex",
+ DIV_CDREX0, 24, 3, CLK_SET_RATE_PARENT, 0),
+ DIV(CLK_DOUT_ACLK_CDREX1, "dout_aclk_cdrex1", "dout_clk2x_phy0",
+ DIV_CDREX0, 16, 3),
+ DIV(CLK_DOUT_CCLK_DREX0, "dout_cclk_drex0", "dout_clk2x_phy0",
+ DIV_CDREX0, 8, 3),
+ DIV(CLK_DOUT_CLK2X_PHY0, "dout_clk2x_phy0", "dout_sclk_cdrex",
+ DIV_CDREX0, 3, 5),
+
+ DIV(CLK_DOUT_PCLK_CORE_MEM, "dout_pclk_core_mem", "mout_mclk_cdrex",
+ DIV_CDREX1, 8, 3),
+
/* Audio Block */
DIV(0, "dout_maudio0", "mout_maudio0", DIV_MAU, 20, 4),
DIV(0, "dout_maupcm0", "dout_maudio0", DIV_MAU, 24, 8),
@@ -1364,6 +1400,7 @@ static void __init exynos5x_clk_init(struct device_node *np,
if (_get_rate("fin_pll") == 24 * MHZ) {
exynos5x_plls[apll].rate_table = exynos5420_pll2550x_24mhz_tbl;
exynos5x_plls[kpll].rate_table = exynos5420_pll2550x_24mhz_tbl;
+ exynos5x_plls[bpll].rate_table = exynos5420_pll2550x_24mhz_tbl;
}
samsung_clk_register_pll(ctx, exynos5x_plls, ARRAY_SIZE(exynos5x_plls),
diff --git a/drivers/clk/samsung/clk-exynos5440.c b/drivers/clk/samsung/clk-exynos5440.c
index a57d01b..a80f3ef 100644
--- a/drivers/clk/samsung/clk-exynos5440.c
+++ b/drivers/clk/samsung/clk-exynos5440.c
@@ -112,6 +112,11 @@ static struct notifier_block exynos5440_clk_restart_handler = {
.priority = 128,
};
+static const struct samsung_pll_clock exynos5440_plls[] __initconst = {
+ PLL(pll_2550x, CLK_CPLLA, "cplla", "xtal", 0, 0x4c, NULL),
+ PLL(pll_2550x, CLK_CPLLB, "cpllb", "xtal", 0, 0x50, NULL),
+};
+
/* register exynos5440 clocks */
static void __init exynos5440_clk_init(struct device_node *np)
{
@@ -129,8 +134,8 @@ static void __init exynos5440_clk_init(struct device_node *np)
samsung_clk_of_register_fixed_ext(ctx, exynos5440_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match);
- samsung_clk_register_pll2550x("cplla", "xtal", reg_base + 0x1c, 0x10);
- samsung_clk_register_pll2550x("cpllb", "xtal", reg_base + 0x20, 0x10);
+ samsung_clk_register_pll(ctx, exynos5440_plls,
+ ARRAY_SIZE(exynos5440_plls), ctx->reg_base);
samsung_clk_register_fixed_rate(ctx, exynos5440_fixed_rate_clks,
ARRAY_SIZE(exynos5440_fixed_rate_clks));
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 48139bd..9617825 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -890,22 +890,14 @@ static const struct clk_ops samsung_s3c2440_mpll_clk_ops = {
#define PLL2550X_M_SHIFT (4)
#define PLL2550X_S_SHIFT (0)
-struct samsung_clk_pll2550x {
- struct clk_hw hw;
- const void __iomem *reg_base;
- unsigned long offset;
-};
-
-#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
-
static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
u32 r, p, m, s, pll_stat;
u64 fvco = parent_rate;
- pll_stat = readl_relaxed(pll->reg_base + pll->offset * 3);
+ pll_stat = readl_relaxed(pll->con_reg);
r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
if (!r)
return 0;
@@ -923,43 +915,6 @@ static const struct clk_ops samsung_pll2550x_clk_ops = {
.recalc_rate = samsung_pll2550x_recalc_rate,
};
-struct clk * __init samsung_clk_register_pll2550x(const char *name,
- const char *pname, const void __iomem *reg_base,
- const unsigned long offset)
-{
- struct samsung_clk_pll2550x *pll;
- struct clk *clk;
- struct clk_init_data init;
-
- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll) {
- pr_err("%s: could not allocate pll clk %s\n", __func__, name);
- return NULL;
- }
-
- init.name = name;
- init.ops = &samsung_pll2550x_clk_ops;
- init.flags = CLK_GET_RATE_NOCACHE;
- init.parent_names = &pname;
- init.num_parents = 1;
-
- pll->hw.init = &init;
- pll->reg_base = reg_base;
- pll->offset = offset;
-
- clk = clk_register(NULL, &pll->hw);
- if (IS_ERR(clk)) {
- pr_err("%s: failed to register pll clock %s\n", __func__,
- name);
- kfree(pll);
- }
-
- if (clk_register_clkdev(clk, name, NULL))
- pr_err("%s: failed to register lookup for %s", __func__, name);
-
- return clk;
-}
-
/*
* PLL2550xx Clock Type
*/
@@ -1063,6 +1018,102 @@ static const struct clk_ops samsung_pll2550xx_clk_min_ops = {
};
/*
+ * PLL2650x Clock Type
+ */
+
+/* Maximum lock time can be 3000 * PDIV cycles */
+#define PLL2650X_LOCK_FACTOR 3000
+
+#define PLL2650X_M_MASK 0x1ff
+#define PLL2650X_P_MASK 0x3f
+#define PLL2650X_S_MASK 0x7
+#define PLL2650X_K_MASK 0xffff
+#define PLL2650X_LOCK_STAT_MASK 0x1
+#define PLL2650X_M_SHIFT 16
+#define PLL2650X_P_SHIFT 8
+#define PLL2650X_S_SHIFT 0
+#define PLL2650X_K_SHIFT 0
+#define PLL2650X_LOCK_STAT_SHIFT 29
+#define PLL2650X_PLL_ENABLE_SHIFT 31
+
+static unsigned long samsung_pll2650x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u64 fout = parent_rate;
+ u32 mdiv, pdiv, sdiv, pll_con0, pll_con1;
+ s16 kdiv;
+
+ pll_con0 = readl_relaxed(pll->con_reg);
+ mdiv = (pll_con0 >> PLL2650X_M_SHIFT) & PLL2650X_M_MASK;
+ pdiv = (pll_con0 >> PLL2650X_P_SHIFT) & PLL2650X_P_MASK;
+ sdiv = (pll_con0 >> PLL2650X_S_SHIFT) & PLL2650X_S_MASK;
+
+ pll_con1 = readl_relaxed(pll->con_reg + 4);
+ kdiv = (s16)((pll_con1 >> PLL2650X_K_SHIFT) & PLL2650X_K_MASK);
+
+ fout *= (mdiv << 16) + kdiv;
+ do_div(fout, (pdiv << sdiv));
+ fout >>= 16;
+
+ return (unsigned long)fout;
+}
+
+static int samsung_pll2650x_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ const struct samsung_pll_rate_table *rate;
+ u32 con0, con1;
+
+ /* Get required rate settings from table */
+ rate = samsung_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;
+ }
+
+ con0 = readl_relaxed(pll->con_reg);
+ con1 = readl_relaxed(pll->con_reg + 4);
+
+ /* Set PLL lock time. */
+ writel_relaxed(rate->pdiv * PLL2650X_LOCK_FACTOR, pll->lock_reg);
+
+ /* Change PLL PMS values */
+ con0 &= ~((PLL2650X_M_MASK << PLL2650X_M_SHIFT) |
+ (PLL2650X_P_MASK << PLL2650X_P_SHIFT) |
+ (PLL2650X_S_MASK << PLL2650X_S_SHIFT));
+ con0 |= (rate->mdiv << PLL2650X_M_SHIFT) |
+ (rate->pdiv << PLL2650X_P_SHIFT) |
+ (rate->sdiv << PLL2650X_S_SHIFT);
+ con0 |= (1 << PLL2650X_PLL_ENABLE_SHIFT);
+ writel_relaxed(con0, pll->con_reg);
+
+ con1 &= ~(PLL2650X_K_MASK << PLL2650X_K_SHIFT);
+ con1 |= ((rate->kdiv & PLL2650X_K_MASK) << PLL2650X_K_SHIFT);
+ writel_relaxed(con1, pll->con_reg + 4);
+
+ do {
+ cpu_relax();
+ con0 = readl_relaxed(pll->con_reg);
+ } while (!(con0 & (PLL2650X_LOCK_STAT_MASK
+ << PLL2650X_LOCK_STAT_SHIFT)));
+
+ return 0;
+}
+
+static const struct clk_ops samsung_pll2650x_clk_ops = {
+ .recalc_rate = samsung_pll2650x_recalc_rate,
+ .round_rate = samsung_pll_round_rate,
+ .set_rate = samsung_pll2650x_set_rate,
+};
+
+static const struct clk_ops samsung_pll2650x_clk_min_ops = {
+ .recalc_rate = samsung_pll2650x_recalc_rate,
+};
+
+/*
* PLL2650XX Clock Type
*/
@@ -1263,12 +1314,21 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
else
init.ops = &samsung_s3c2440_mpll_clk_ops;
break;
+ case pll_2550x:
+ init.ops = &samsung_pll2550x_clk_ops;
+ break;
case pll_2550xx:
if (!pll->rate_table)
init.ops = &samsung_pll2550xx_clk_min_ops;
else
init.ops = &samsung_pll2550xx_clk_ops;
break;
+ case pll_2650x:
+ if (!pll->rate_table)
+ init.ops = &samsung_pll2650x_clk_min_ops;
+ else
+ init.ops = &samsung_pll2650x_clk_ops;
+ break;
case pll_2650xx:
if (!pll->rate_table)
init.ops = &samsung_pll2650xx_clk_min_ops;
diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h
index 213de9a..a1ca023 100644
--- a/drivers/clk/samsung/clk-pll.h
+++ b/drivers/clk/samsung/clk-pll.h
@@ -31,7 +31,9 @@ enum samsung_pll_type {
pll_s3c2410_mpll,
pll_s3c2410_upll,
pll_s3c2440_mpll,
+ pll_2550x,
pll_2550xx,
+ pll_2650x,
pll_2650xx,
pll_1450x,
pll_1451x,
diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c
index 546bd79..a485f3b 100644
--- a/drivers/clk/st/clk-flexgen.c
+++ b/drivers/clk/st/clk-flexgen.c
@@ -15,6 +15,11 @@
#include <linux/of.h>
#include <linux/of_address.h>
+struct clkgen_data {
+ unsigned long flags;
+ bool mode;
+};
+
struct flexgen {
struct clk_hw hw;
@@ -28,9 +33,14 @@ struct flexgen {
struct clk_gate fgate;
/* Final divisor */
struct clk_divider fdiv;
+ /* Asynchronous mode control */
+ struct clk_gate sync;
+ /* hw control flags */
+ bool control_mode;
};
#define to_flexgen(_hw) container_of(_hw, struct flexgen, hw)
+#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
static int flexgen_enable(struct clk_hw *hw)
{
@@ -139,12 +149,21 @@ static int flexgen_set_rate(struct clk_hw *hw, unsigned long rate,
struct flexgen *flexgen = to_flexgen(hw);
struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
+ struct clk_hw *sync_hw = &flexgen->sync.hw;
+ struct clk_gate *config = to_clk_gate(sync_hw);
unsigned long div = 0;
int ret = 0;
+ u32 reg;
__clk_hw_set_clk(pdiv_hw, hw);
__clk_hw_set_clk(fdiv_hw, hw);
+ if (flexgen->control_mode) {
+ reg = readl(config->reg);
+ reg &= ~BIT(config->bit_idx);
+ writel(reg, config->reg);
+ }
+
div = clk_best_div(parent_rate, rate);
/*
@@ -178,7 +197,7 @@ static const struct clk_ops flexgen_ops = {
static struct clk *clk_register_flexgen(const char *name,
const char **parent_names, u8 num_parents,
void __iomem *reg, spinlock_t *lock, u32 idx,
- unsigned long flexgen_flags) {
+ unsigned long flexgen_flags, bool mode) {
struct flexgen *fgxbar;
struct clk *clk;
struct clk_init_data init;
@@ -227,6 +246,13 @@ static struct clk *clk_register_flexgen(const char *name,
fgxbar->fdiv.reg = fdiv_reg;
fgxbar->fdiv.width = 6;
+ /* Final divider sync config */
+ fgxbar->sync.lock = lock;
+ fgxbar->sync.reg = fdiv_reg;
+ fgxbar->sync.bit_idx = 7;
+
+ fgxbar->control_mode = mode;
+
fgxbar->hw.init = &init;
clk = clk_register(NULL, &fgxbar->hw);
@@ -259,6 +285,27 @@ static const char ** __init flexgen_get_parents(struct device_node *np,
return parents;
}
+static const struct clkgen_data clkgen_audio = {
+ .flags = CLK_SET_RATE_PARENT,
+};
+
+static const struct clkgen_data clkgen_video = {
+ .flags = CLK_SET_RATE_PARENT,
+ .mode = 1,
+};
+
+static const struct of_device_id flexgen_of_match[] = {
+ {
+ .compatible = "st,flexgen-audio",
+ .data = &clkgen_audio,
+ },
+ {
+ .compatible = "st,flexgen-video",
+ .data = &clkgen_video,
+ },
+ {}
+};
+
static void __init st_of_flexgen_setup(struct device_node *np)
{
struct device_node *pnode;
@@ -267,7 +314,11 @@ static void __init st_of_flexgen_setup(struct device_node *np)
const char **parents;
int num_parents, i;
spinlock_t *rlock = NULL;
+ const struct of_device_id *match;
+ struct clkgen_data *data = NULL;
+ unsigned long flex_flags = 0;
int ret;
+ bool clk_mode = 0;
pnode = of_get_parent(np);
if (!pnode)
@@ -281,6 +332,13 @@ static void __init st_of_flexgen_setup(struct device_node *np)
if (!parents)
return;
+ match = of_match_node(flexgen_of_match, np);
+ if (match) {
+ data = (struct clkgen_data *)match->data;
+ flex_flags = data->flags;
+ clk_mode = data->mode;
+ }
+
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
goto err;
@@ -307,7 +365,6 @@ static void __init st_of_flexgen_setup(struct device_node *np)
for (i = 0; i < clk_data->clk_num; i++) {
struct clk *clk;
const char *clk_name;
- unsigned long flex_flags = 0;
if (of_property_read_string_index(np, "clock-output-names",
i, &clk_name)) {
@@ -323,7 +380,7 @@ static void __init st_of_flexgen_setup(struct device_node *np)
continue;
clk = clk_register_flexgen(clk_name, parents, num_parents,
- reg, rlock, i, flex_flags);
+ reg, rlock, i, flex_flags, clk_mode);
if (IS_ERR(clk))
goto err;
diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c
index 09afeb8..14819d9 100644
--- a/drivers/clk/st/clkgen-fsyn.c
+++ b/drivers/clk/st/clkgen-fsyn.c
@@ -42,79 +42,6 @@ struct stm_fs {
unsigned long nsdiv;
};
-static const struct stm_fs fs216c65_rtbl[] = {
- { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 312.5 Khz */
- { .mdiv = 0x17, .pe = 0x25ed, .sdiv = 0x1, .nsdiv = 0 }, /* 27 MHz */
- { .mdiv = 0x1a, .pe = 0x7b36, .sdiv = 0x2, .nsdiv = 1 }, /* 36.87 MHz */
- { .mdiv = 0x13, .pe = 0x0, .sdiv = 0x2, .nsdiv = 1 }, /* 48 MHz */
- { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x1, .nsdiv = 1 }, /* 108 MHz */
-};
-
-static const struct stm_fs fs432c65_rtbl[] = {
- { .mdiv = 0x1f, .pe = 0x0, .sdiv = 0x7, .nsdiv = 0 }, /* 625 Khz */
- { .mdiv = 0x13, .pe = 0x777c, .sdiv = 0x4, .nsdiv = 1 }, /* 25.175 MHz */
- { .mdiv = 0x19, .pe = 0x4d35, .sdiv = 0x2, .nsdiv = 0 }, /* 25.200 MHz */
- { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x4, .nsdiv = 1 }, /* 27.000 MHz */
- { .mdiv = 0x17, .pe = 0x28f5, .sdiv = 0x2, .nsdiv = 0 }, /* 27.027 MHz */
- { .mdiv = 0x16, .pe = 0x3359, .sdiv = 0x2, .nsdiv = 0 }, /* 28.320 MHz */
- { .mdiv = 0x1f, .pe = 0x2083, .sdiv = 0x3, .nsdiv = 1 }, /* 30.240 MHz */
- { .mdiv = 0x1e, .pe = 0x430d, .sdiv = 0x3, .nsdiv = 1 }, /* 31.500 MHz */
- { .mdiv = 0x17, .pe = 0x0, .sdiv = 0x3, .nsdiv = 1 }, /* 40.000 MHz */
- { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x1, .nsdiv = 0 }, /* 49.500 MHz */
- { .mdiv = 0x13, .pe = 0x6667, .sdiv = 0x3, .nsdiv = 1 }, /* 50.000 MHz */
- { .mdiv = 0x10, .pe = 0x1ee6, .sdiv = 0x3, .nsdiv = 1 }, /* 57.284 MHz */
- { .mdiv = 0x1d, .pe = 0x3b14, .sdiv = 0x2, .nsdiv = 1 }, /* 65.000 MHz */
- { .mdiv = 0x12, .pe = 0x7c65, .sdiv = 0x1, .nsdiv = 0 }, /* 71.000 MHz */
- { .mdiv = 0x19, .pe = 0xecd, .sdiv = 0x2, .nsdiv = 1 }, /* 74.176 MHz */
- { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x2, .nsdiv = 1 }, /* 74.250 MHz */
- { .mdiv = 0x19, .pe = 0x3334, .sdiv = 0x2, .nsdiv = 1 }, /* 75.000 MHz */
- { .mdiv = 0x18, .pe = 0x5138, .sdiv = 0x2, .nsdiv = 1 }, /* 78.800 MHz */
- { .mdiv = 0x1d, .pe = 0x77d, .sdiv = 0x0, .nsdiv = 0 }, /* 85.500 MHz */
- { .mdiv = 0x1c, .pe = 0x13d5, .sdiv = 0x0, .nsdiv = 0 }, /* 88.750 MHz */
- { .mdiv = 0x11, .pe = 0x1c72, .sdiv = 0x2, .nsdiv = 1 }, /* 108.000 MHz */
- { .mdiv = 0x17, .pe = 0x28f5, .sdiv = 0x0, .nsdiv = 0 }, /* 108.108 MHz */
- { .mdiv = 0x10, .pe = 0x6e26, .sdiv = 0x2, .nsdiv = 1 }, /* 118.963 MHz */
- { .mdiv = 0x15, .pe = 0x3e63, .sdiv = 0x0, .nsdiv = 0 }, /* 119.000 MHz */
- { .mdiv = 0x1c, .pe = 0x471d, .sdiv = 0x1, .nsdiv = 1 }, /* 135.000 MHz */
- { .mdiv = 0x19, .pe = 0xecd, .sdiv = 0x1, .nsdiv = 1 }, /* 148.352 MHz */
- { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x1, .nsdiv = 1 }, /* 148.500 MHz */
- { .mdiv = 0x19, .pe = 0x121a, .sdiv = 0x0, .nsdiv = 1 }, /* 297 MHz */
-};
-
-static const struct stm_fs fs660c32_rtbl[] = {
- { .mdiv = 0x14, .pe = 0x376b, .sdiv = 0x4, .nsdiv = 1 }, /* 25.175 MHz */
- { .mdiv = 0x14, .pe = 0x30c3, .sdiv = 0x4, .nsdiv = 1 }, /* 25.200 MHz */
- { .mdiv = 0x10, .pe = 0x71c7, .sdiv = 0x4, .nsdiv = 1 }, /* 27.000 MHz */
- { .mdiv = 0x00, .pe = 0x47af, .sdiv = 0x3, .nsdiv = 0 }, /* 27.027 MHz */
- { .mdiv = 0x0e, .pe = 0x4e1a, .sdiv = 0x4, .nsdiv = 1 }, /* 28.320 MHz */
- { .mdiv = 0x0b, .pe = 0x534d, .sdiv = 0x4, .nsdiv = 1 }, /* 30.240 MHz */
- { .mdiv = 0x17, .pe = 0x6fbf, .sdiv = 0x2, .nsdiv = 0 }, /* 31.500 MHz */
- { .mdiv = 0x01, .pe = 0x0, .sdiv = 0x4, .nsdiv = 1 }, /* 40.000 MHz */
- { .mdiv = 0x15, .pe = 0x2aab, .sdiv = 0x3, .nsdiv = 1 }, /* 49.500 MHz */
- { .mdiv = 0x14, .pe = 0x6666, .sdiv = 0x3, .nsdiv = 1 }, /* 50.000 MHz */
- { .mdiv = 0x1d, .pe = 0x395f, .sdiv = 0x1, .nsdiv = 0 }, /* 57.284 MHz */
- { .mdiv = 0x08, .pe = 0x4ec5, .sdiv = 0x3, .nsdiv = 1 }, /* 65.000 MHz */
- { .mdiv = 0x05, .pe = 0x1770, .sdiv = 0x3, .nsdiv = 1 }, /* 71.000 MHz */
- { .mdiv = 0x03, .pe = 0x4ba7, .sdiv = 0x3, .nsdiv = 1 }, /* 74.176 MHz */
- { .mdiv = 0x0f, .pe = 0x3426, .sdiv = 0x1, .nsdiv = 0 }, /* 74.250 MHz */
- { .mdiv = 0x0e, .pe = 0x7777, .sdiv = 0x1, .nsdiv = 0 }, /* 75.000 MHz */
- { .mdiv = 0x01, .pe = 0x4053, .sdiv = 0x3, .nsdiv = 1 }, /* 78.800 MHz */
- { .mdiv = 0x09, .pe = 0x15b5, .sdiv = 0x1, .nsdiv = 0 }, /* 85.500 MHz */
- { .mdiv = 0x1b, .pe = 0x3f19, .sdiv = 0x2, .nsdiv = 1 }, /* 88.750 MHz */
- { .mdiv = 0x10, .pe = 0x71c7, .sdiv = 0x2, .nsdiv = 1 }, /* 108.000 MHz */
- { .mdiv = 0x00, .pe = 0x47af, .sdiv = 0x1, .nsdiv = 0 }, /* 108.108 MHz */
- { .mdiv = 0x0c, .pe = 0x3118, .sdiv = 0x2, .nsdiv = 1 }, /* 118.963 MHz */
- { .mdiv = 0x0c, .pe = 0x2f54, .sdiv = 0x2, .nsdiv = 1 }, /* 119.000 MHz */
- { .mdiv = 0x07, .pe = 0xe39, .sdiv = 0x2, .nsdiv = 1 }, /* 135.000 MHz */
- { .mdiv = 0x03, .pe = 0x4ba7, .sdiv = 0x2, .nsdiv = 1 }, /* 148.352 MHz */
- { .mdiv = 0x0f, .pe = 0x3426, .sdiv = 0x0, .nsdiv = 0 }, /* 148.500 MHz */
- { .mdiv = 0x03, .pe = 0x4ba7, .sdiv = 0x1, .nsdiv = 1 }, /* 296.704 MHz */
- { .mdiv = 0x03, .pe = 0x471c, .sdiv = 0x1, .nsdiv = 1 }, /* 297.000 MHz */
- { .mdiv = 0x00, .pe = 0x295f, .sdiv = 0x1, .nsdiv = 1 }, /* 326.700 MHz */
- { .mdiv = 0x1f, .pe = 0x3633, .sdiv = 0x0, .nsdiv = 1 }, /* 333.000 MHz */
- { .mdiv = 0x1c, .pe = 0x0, .sdiv = 0x0, .nsdiv = 1 }, /* 352.000 Mhz */
-};
-
struct clkgen_quadfs_data {
bool reset_present;
bool bwfilter_present;
@@ -138,174 +65,18 @@ struct clkgen_quadfs_data {
struct clkgen_field nsdiv[QUADFS_MAX_CHAN];
const struct clk_ops *pll_ops;
- const struct stm_fs *rtbl;
- u8 rtbl_cnt;
+ int (*get_params)(unsigned long, unsigned long, struct stm_fs *);
int (*get_rate)(unsigned long , const struct stm_fs *,
unsigned long *);
};
-static const struct clk_ops st_quadfs_pll_c65_ops;
static const struct clk_ops st_quadfs_pll_c32_ops;
-static const struct clk_ops st_quadfs_fs216c65_ops;
-static const struct clk_ops st_quadfs_fs432c65_ops;
static const struct clk_ops st_quadfs_fs660c32_ops;
-static int clk_fs216c65_get_rate(unsigned long, const struct stm_fs *,
- unsigned long *);
-static int clk_fs432c65_get_rate(unsigned long, const struct stm_fs *,
- unsigned long *);
+static int clk_fs660c32_dig_get_params(unsigned long input,
+ unsigned long output, struct stm_fs *fs);
static int clk_fs660c32_dig_get_rate(unsigned long, const struct stm_fs *,
unsigned long *);
-/*
- * Values for all of the standalone instances of this clock
- * generator found in STiH415 and STiH416 SYSCFG register banks. Note
- * that the individual channel standby control bits (nsb) are in the
- * first register along with the PLL control bits.
- */
-static const struct clkgen_quadfs_data st_fs216c65_416 = {
- /* 416 specific */
- .npda = CLKGEN_FIELD(0x0, 0x1, 14),
- .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
- CLKGEN_FIELD(0x0, 0x1, 11),
- CLKGEN_FIELD(0x0, 0x1, 12),
- CLKGEN_FIELD(0x0, 0x1, 13) },
- .nsdiv_present = true,
- .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
- CLKGEN_FIELD(0x0, 0x1, 19),
- CLKGEN_FIELD(0x0, 0x1, 20),
- CLKGEN_FIELD(0x0, 0x1, 21) },
- .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
- CLKGEN_FIELD(0x14, 0x1f, 0),
- CLKGEN_FIELD(0x24, 0x1f, 0),
- CLKGEN_FIELD(0x34, 0x1f, 0) },
- .en = { CLKGEN_FIELD(0x10, 0x1, 0),
- CLKGEN_FIELD(0x20, 0x1, 0),
- CLKGEN_FIELD(0x30, 0x1, 0),
- CLKGEN_FIELD(0x40, 0x1, 0) },
- .ndiv = CLKGEN_FIELD(0x0, 0x1, 15),
- .bwfilter_present = true,
- .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16),
- .pe = { CLKGEN_FIELD(0x8, 0xffff, 0),
- CLKGEN_FIELD(0x18, 0xffff, 0),
- CLKGEN_FIELD(0x28, 0xffff, 0),
- CLKGEN_FIELD(0x38, 0xffff, 0) },
- .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0),
- CLKGEN_FIELD(0x1C, 0x7, 0),
- CLKGEN_FIELD(0x2C, 0x7, 0),
- CLKGEN_FIELD(0x3C, 0x7, 0) },
- .pll_ops = &st_quadfs_pll_c65_ops,
- .rtbl = fs216c65_rtbl,
- .rtbl_cnt = ARRAY_SIZE(fs216c65_rtbl),
- .get_rate = clk_fs216c65_get_rate,
-};
-
-static const struct clkgen_quadfs_data st_fs432c65_416 = {
- .npda = CLKGEN_FIELD(0x0, 0x1, 14),
- .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
- CLKGEN_FIELD(0x0, 0x1, 11),
- CLKGEN_FIELD(0x0, 0x1, 12),
- CLKGEN_FIELD(0x0, 0x1, 13) },
- .nsdiv_present = true,
- .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
- CLKGEN_FIELD(0x0, 0x1, 19),
- CLKGEN_FIELD(0x0, 0x1, 20),
- CLKGEN_FIELD(0x0, 0x1, 21) },
- .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
- CLKGEN_FIELD(0x14, 0x1f, 0),
- CLKGEN_FIELD(0x24, 0x1f, 0),
- CLKGEN_FIELD(0x34, 0x1f, 0) },
- .en = { CLKGEN_FIELD(0x10, 0x1, 0),
- CLKGEN_FIELD(0x20, 0x1, 0),
- CLKGEN_FIELD(0x30, 0x1, 0),
- CLKGEN_FIELD(0x40, 0x1, 0) },
- .ndiv = CLKGEN_FIELD(0x0, 0x1, 15),
- .bwfilter_present = true,
- .ref_bw = CLKGEN_FIELD(0x0, 0x3, 16),
- .pe = { CLKGEN_FIELD(0x8, 0xffff, 0),
- CLKGEN_FIELD(0x18, 0xffff, 0),
- CLKGEN_FIELD(0x28, 0xffff, 0),
- CLKGEN_FIELD(0x38, 0xffff, 0) },
- .sdiv = { CLKGEN_FIELD(0xC, 0x7, 0),
- CLKGEN_FIELD(0x1C, 0x7, 0),
- CLKGEN_FIELD(0x2C, 0x7, 0),
- CLKGEN_FIELD(0x3C, 0x7, 0) },
- .pll_ops = &st_quadfs_pll_c65_ops,
- .rtbl = fs432c65_rtbl,
- .rtbl_cnt = ARRAY_SIZE(fs432c65_rtbl),
- .get_rate = clk_fs432c65_get_rate,
-};
-
-static const struct clkgen_quadfs_data st_fs660c32_E_416 = {
- .npda = CLKGEN_FIELD(0x0, 0x1, 14),
- .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
- CLKGEN_FIELD(0x0, 0x1, 11),
- CLKGEN_FIELD(0x0, 0x1, 12),
- CLKGEN_FIELD(0x0, 0x1, 13) },
- .nsdiv_present = true,
- .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
- CLKGEN_FIELD(0x0, 0x1, 19),
- CLKGEN_FIELD(0x0, 0x1, 20),
- CLKGEN_FIELD(0x0, 0x1, 21) },
- .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
- CLKGEN_FIELD(0x14, 0x1f, 0),
- CLKGEN_FIELD(0x24, 0x1f, 0),
- CLKGEN_FIELD(0x34, 0x1f, 0) },
- .en = { CLKGEN_FIELD(0x10, 0x1, 0),
- CLKGEN_FIELD(0x20, 0x1, 0),
- CLKGEN_FIELD(0x30, 0x1, 0),
- CLKGEN_FIELD(0x40, 0x1, 0) },
- .ndiv = CLKGEN_FIELD(0x0, 0x7, 15),
- .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0),
- CLKGEN_FIELD(0x18, 0x7fff, 0),
- CLKGEN_FIELD(0x28, 0x7fff, 0),
- CLKGEN_FIELD(0x38, 0x7fff, 0) },
- .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0),
- CLKGEN_FIELD(0x1C, 0xf, 0),
- CLKGEN_FIELD(0x2C, 0xf, 0),
- CLKGEN_FIELD(0x3C, 0xf, 0) },
- .lockstatus_present = true,
- .lock_status = CLKGEN_FIELD(0xAC, 0x1, 0),
- .pll_ops = &st_quadfs_pll_c32_ops,
- .rtbl = fs660c32_rtbl,
- .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl),
- .get_rate = clk_fs660c32_dig_get_rate,
-};
-
-static const struct clkgen_quadfs_data st_fs660c32_F_416 = {
- .npda = CLKGEN_FIELD(0x0, 0x1, 14),
- .nsb = { CLKGEN_FIELD(0x0, 0x1, 10),
- CLKGEN_FIELD(0x0, 0x1, 11),
- CLKGEN_FIELD(0x0, 0x1, 12),
- CLKGEN_FIELD(0x0, 0x1, 13) },
- .nsdiv_present = true,
- .nsdiv = { CLKGEN_FIELD(0x0, 0x1, 18),
- CLKGEN_FIELD(0x0, 0x1, 19),
- CLKGEN_FIELD(0x0, 0x1, 20),
- CLKGEN_FIELD(0x0, 0x1, 21) },
- .mdiv = { CLKGEN_FIELD(0x4, 0x1f, 0),
- CLKGEN_FIELD(0x14, 0x1f, 0),
- CLKGEN_FIELD(0x24, 0x1f, 0),
- CLKGEN_FIELD(0x34, 0x1f, 0) },
- .en = { CLKGEN_FIELD(0x10, 0x1, 0),
- CLKGEN_FIELD(0x20, 0x1, 0),
- CLKGEN_FIELD(0x30, 0x1, 0),
- CLKGEN_FIELD(0x40, 0x1, 0) },
- .ndiv = CLKGEN_FIELD(0x0, 0x7, 15),
- .pe = { CLKGEN_FIELD(0x8, 0x7fff, 0),
- CLKGEN_FIELD(0x18, 0x7fff, 0),
- CLKGEN_FIELD(0x28, 0x7fff, 0),
- CLKGEN_FIELD(0x38, 0x7fff, 0) },
- .sdiv = { CLKGEN_FIELD(0xC, 0xf, 0),
- CLKGEN_FIELD(0x1C, 0xf, 0),
- CLKGEN_FIELD(0x2C, 0xf, 0),
- CLKGEN_FIELD(0x3C, 0xf, 0) },
- .lockstatus_present = true,
- .lock_status = CLKGEN_FIELD(0xEC, 0x1, 0),
- .pll_ops = &st_quadfs_pll_c32_ops,
- .rtbl = fs660c32_rtbl,
- .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl),
- .get_rate = clk_fs660c32_dig_get_rate,
-};
static const struct clkgen_quadfs_data st_fs660c32_C = {
.nrst_present = true,
@@ -345,8 +116,7 @@ static const struct clkgen_quadfs_data st_fs660c32_C = {
.powerup_polarity = 1,
.standby_polarity = 1,
.pll_ops = &st_quadfs_pll_c32_ops,
- .rtbl = fs660c32_rtbl,
- .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl),
+ .get_params = clk_fs660c32_dig_get_params,
.get_rate = clk_fs660c32_dig_get_rate,
};
@@ -388,8 +158,7 @@ static const struct clkgen_quadfs_data st_fs660c32_D = {
.powerup_polarity = 1,
.standby_polarity = 1,
.pll_ops = &st_quadfs_pll_c32_ops,
- .rtbl = fs660c32_rtbl,
- .rtbl_cnt = ARRAY_SIZE(fs660c32_rtbl),
+ .get_params = clk_fs660c32_dig_get_params,
.get_rate = clk_fs660c32_dig_get_rate,};
/**
@@ -605,12 +374,6 @@ static int quadfs_pll_fs660c32_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
-static const struct clk_ops st_quadfs_pll_c65_ops = {
- .enable = quadfs_pll_enable,
- .disable = quadfs_pll_disable,
- .is_enabled = quadfs_pll_is_enabled,
-};
-
static const struct clk_ops st_quadfs_pll_c32_ops = {
.enable = quadfs_pll_enable,
.disable = quadfs_pll_disable,
@@ -797,48 +560,6 @@ static int quadfs_fsynth_is_enabled(struct clk_hw *hw)
return fs->data->standby_polarity ? !nsb : !!nsb;
}
-#define P15 (uint64_t)(1 << 15)
-
-static int clk_fs216c65_get_rate(unsigned long input, const struct stm_fs *fs,
- unsigned long *rate)
-{
- uint64_t res;
- unsigned long ns;
- unsigned long nd = 8; /* ndiv stuck at 0 => val = 8 */
- unsigned long s;
- long m;
-
- m = fs->mdiv - 32;
- s = 1 << (fs->sdiv + 1);
- ns = (fs->nsdiv ? 1 : 3);
-
- res = (uint64_t)(s * ns * P15 * (uint64_t)(m + 33));
- res = res - (s * ns * fs->pe);
- *rate = div64_u64(P15 * nd * input * 32, res);
-
- return 0;
-}
-
-static int clk_fs432c65_get_rate(unsigned long input, const struct stm_fs *fs,
- unsigned long *rate)
-{
- uint64_t res;
- unsigned long nd = 16; /* ndiv value; stuck at 0 (30Mhz input) */
- long m;
- unsigned long sd;
- unsigned long ns;
-
- m = fs->mdiv - 32;
- sd = 1 << (fs->sdiv + 1);
- ns = (fs->nsdiv ? 1 : 3);
-
- res = (uint64_t)(sd * ns * P15 * (uint64_t)(m + 33));
- res = res - (sd * ns * fs->pe);
- *rate = div64_u64(P15 * nd * input * 32, res);
-
- return 0;
-}
-
#define P20 (uint64_t)(1 << 20)
static int clk_fs660c32_dig_get_rate(unsigned long input,
@@ -864,6 +585,107 @@ static int clk_fs660c32_dig_get_rate(unsigned long input,
return 0;
}
+
+static int clk_fs660c32_get_pe(int m, int si, unsigned long *deviation,
+ signed long input, unsigned long output, uint64_t *p,
+ struct stm_fs *fs)
+{
+ unsigned long new_freq, new_deviation;
+ struct stm_fs fs_tmp;
+ uint64_t val;
+
+ val = (uint64_t)output << si;
+
+ *p = (uint64_t)input * P20 - (32LL + (uint64_t)m) * val * (P20 / 32LL);
+
+ *p = div64_u64(*p, val);
+
+ if (*p > 32767LL)
+ return 1;
+
+ fs_tmp.mdiv = (unsigned long) m;
+ fs_tmp.pe = (unsigned long)*p;
+ fs_tmp.sdiv = si;
+ fs_tmp.nsdiv = 1;
+
+ clk_fs660c32_dig_get_rate(input, &fs_tmp, &new_freq);
+
+ new_deviation = abs(output - new_freq);
+
+ if (new_deviation < *deviation) {
+ fs->mdiv = m;
+ fs->pe = (unsigned long)*p;
+ fs->sdiv = si;
+ fs->nsdiv = 1;
+ *deviation = new_deviation;
+ }
+ return 0;
+}
+
+static int clk_fs660c32_dig_get_params(unsigned long input,
+ unsigned long output, struct stm_fs *fs)
+{
+ int si; /* sdiv_reg (8 downto 0) */
+ int m; /* md value */
+ unsigned long new_freq, new_deviation;
+ /* initial condition to say: "infinite deviation" */
+ unsigned long deviation = ~0;
+ uint64_t p, p1, p2; /* pe value */
+ int r1, r2;
+
+ struct stm_fs fs_tmp;
+
+ for (si = 0; (si <= 8) && deviation; si++) {
+
+ /* Boundary test to avoid useless iteration */
+ r1 = clk_fs660c32_get_pe(0, si, &deviation,
+ input, output, &p1, fs);
+ r2 = clk_fs660c32_get_pe(31, si, &deviation,
+ input, output, &p2, fs);
+
+ /* No solution */
+ if (r1 && r2 && (p1 > p2))
+ continue;
+
+ /* Try to find best deviation */
+ for (m = 1; (m < 31) && deviation; m++)
+ clk_fs660c32_get_pe(m, si, &deviation,
+ input, output, &p, fs);
+
+ }
+
+ if (deviation == ~0) /* No solution found */
+ return -1;
+
+ /* pe fine tuning if deviation not 0: +/- 2 around computed pe value */
+ if (deviation) {
+ fs_tmp.mdiv = fs->mdiv;
+ fs_tmp.sdiv = fs->sdiv;
+ fs_tmp.nsdiv = fs->nsdiv;
+
+ if (fs->pe > 2)
+ p2 = fs->pe - 2;
+ else
+ p2 = 0;
+
+ for (; p2 < 32768ll && (p2 <= (fs->pe + 2)); p2++) {
+ fs_tmp.pe = (unsigned long)p2;
+
+ clk_fs660c32_dig_get_rate(input, &fs_tmp, &new_freq);
+
+ new_deviation = abs(output - new_freq);
+
+ /* Check if this is a better solution */
+ if (new_deviation < deviation) {
+ fs->pe = (unsigned long)p2;
+ deviation = new_deviation;
+
+ }
+ }
+ }
+ return 0;
+}
+
static int quadfs_fsynt_get_hw_value_for_recalc(struct st_clk_quadfs_fsynth *fs,
struct stm_fs *params)
{
@@ -899,38 +721,14 @@ static long quadfs_find_best_rate(struct clk_hw *hw, unsigned long drate,
struct st_clk_quadfs_fsynth *fs = to_quadfs_fsynth(hw);
int (*clk_fs_get_rate)(unsigned long ,
const struct stm_fs *, unsigned long *);
- struct stm_fs prev_params;
- unsigned long prev_rate, rate = 0;
- unsigned long diff_rate, prev_diff_rate = ~0;
- int index;
+ int (*clk_fs_get_params)(unsigned long, unsigned long, struct stm_fs *);
+ unsigned long rate = 0;
clk_fs_get_rate = fs->data->get_rate;
+ clk_fs_get_params = fs->data->get_params;
- for (index = 0; index < fs->data->rtbl_cnt; index++) {
- prev_rate = rate;
-
- *params = fs->data->rtbl[index];
- prev_params = *params;
-
- clk_fs_get_rate(prate, &fs->data->rtbl[index], &rate);
-
- diff_rate = abs(drate - rate);
-
- if (diff_rate > prev_diff_rate) {
- rate = prev_rate;
- *params = prev_params;
- break;
- }
-
- prev_diff_rate = diff_rate;
-
- if (drate == rate)
- return rate;
- }
-
-
- if (index == fs->data->rtbl_cnt)
- *params = prev_params;
+ if (!clk_fs_get_params(prate, drate, params))
+ clk_fs_get_rate(prate, params, &rate);
return rate;
}
@@ -1063,34 +861,6 @@ static struct clk * __init st_clk_register_quadfs_fsynth(
return clk;
}
-static const struct of_device_id quadfs_of_match[] = {
- {
- .compatible = "st,stih416-quadfs216",
- .data = &st_fs216c65_416
- },
- {
- .compatible = "st,stih416-quadfs432",
- .data = &st_fs432c65_416
- },
- {
- .compatible = "st,stih416-quadfs660-E",
- .data = &st_fs660c32_E_416
- },
- {
- .compatible = "st,stih416-quadfs660-F",
- .data = &st_fs660c32_F_416
- },
- {
- .compatible = "st,stih407-quadfs660-C",
- .data = &st_fs660c32_C
- },
- {
- .compatible = "st,stih407-quadfs660-D",
- .data = &st_fs660c32_D
- },
- {}
-};
-
static void __init st_of_create_quadfs_fsynths(
struct device_node *np, const char *pll_name,
struct clkgen_quadfs_data *quadfs, void __iomem *reg,
@@ -1150,18 +920,14 @@ static void __init st_of_create_quadfs_fsynths(
of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
}
-static void __init st_of_quadfs_setup(struct device_node *np)
+static void __init st_of_quadfs_setup(struct device_node *np,
+ struct clkgen_quadfs_data *data)
{
- const struct of_device_id *match;
struct clk *clk;
const char *pll_name, *clk_parent_name;
void __iomem *reg;
spinlock_t *lock;
- match = of_match_node(quadfs_of_match, np);
- if (WARN_ON(!match))
- return;
-
reg = of_iomap(np, 0);
if (!reg)
return;
@@ -1180,8 +946,8 @@ static void __init st_of_quadfs_setup(struct device_node *np)
spin_lock_init(lock);
- clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name,
- (struct clkgen_quadfs_data *) match->data, reg, lock);
+ clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name, data,
+ reg, lock);
if (IS_ERR(clk))
goto err_exit;
else
@@ -1190,11 +956,20 @@ static void __init st_of_quadfs_setup(struct device_node *np)
__clk_get_name(clk_get_parent(clk)),
(unsigned int)clk_get_rate(clk));
- st_of_create_quadfs_fsynths(np, pll_name,
- (struct clkgen_quadfs_data *)match->data,
- reg, lock);
+ st_of_create_quadfs_fsynths(np, pll_name, data, reg, lock);
err_exit:
kfree(pll_name); /* No longer need local copy of the PLL name */
}
-CLK_OF_DECLARE(quadfs, "st,quadfs", st_of_quadfs_setup);
+
+static void __init st_of_quadfs660C_setup(struct device_node *np)
+{
+ st_of_quadfs_setup(np, (struct clkgen_quadfs_data *) &st_fs660c32_C);
+}
+CLK_OF_DECLARE(quadfs660C, "st,quadfs-pll", st_of_quadfs660C_setup);
+
+static void __init st_of_quadfs660D_setup(struct device_node *np)
+{
+ st_of_quadfs_setup(np, (struct clkgen_quadfs_data *) &st_fs660c32_D);
+}
+CLK_OF_DECLARE(quadfs660D, "st,quadfs", st_of_quadfs660D_setup);
diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c
index b1e10ff..c514d39 100644
--- a/drivers/clk/st/clkgen-mux.c
+++ b/drivers/clk/st/clkgen-mux.c
@@ -19,9 +19,6 @@
#include <linux/clk-provider.h>
#include "clkgen.h"
-static DEFINE_SPINLOCK(clkgena_divmux_lock);
-static DEFINE_SPINLOCK(clkgenf_lock);
-
static const char ** __init clkgen_mux_get_parents(struct device_node *np,
int *num_parents)
{
@@ -40,498 +37,6 @@ static const char ** __init clkgen_mux_get_parents(struct device_node *np,
return parents;
}
-/**
- * DOC: Clock mux with a programmable divider on each of its three inputs.
- * The mux has an input setting which effectively gates its output.
- *
- * Traits of this clock:
- * prepare - clk_(un)prepare only ensures parent is (un)prepared
- * enable - clk_enable and clk_disable are functional & control gating
- * rate - set rate is supported
- * parent - set/get parent
- */
-
-#define NUM_INPUTS 3
-
-struct clkgena_divmux {
- struct clk_hw hw;
- /* Subclassed mux and divider structures */
- struct clk_mux mux;
- struct clk_divider div[NUM_INPUTS];
- /* Enable/running feedback register bits for each input */
- void __iomem *feedback_reg[NUM_INPUTS];
- int feedback_bit_idx;
-
- u8 muxsel;
-};
-
-#define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw)
-
-struct clkgena_divmux_data {
- int num_outputs;
- int mux_offset;
- int mux_offset2;
- int mux_start_bit;
- int div_offsets[NUM_INPUTS];
- int fb_offsets[NUM_INPUTS];
- int fb_start_bit_idx;
-};
-
-#define CKGAX_CLKOPSRC_SWITCH_OFF 0x3
-
-static int clkgena_divmux_is_running(struct clkgena_divmux *mux)
-{
- u32 regval = readl(mux->feedback_reg[mux->muxsel]);
- u32 running = regval & BIT(mux->feedback_bit_idx);
- return !!running;
-}
-
-static int clkgena_divmux_enable(struct clk_hw *hw)
-{
- struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
- struct clk_hw *mux_hw = &genamux->mux.hw;
- unsigned long timeout;
- int ret = 0;
-
- __clk_hw_set_clk(mux_hw, hw);
-
- ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel);
- if (ret)
- return ret;
-
- timeout = jiffies + msecs_to_jiffies(10);
-
- while (!clkgena_divmux_is_running(genamux)) {
- if (time_after(jiffies, timeout))
- return -ETIMEDOUT;
- cpu_relax();
- }
-
- return 0;
-}
-
-static void clkgena_divmux_disable(struct clk_hw *hw)
-{
- struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
- struct clk_hw *mux_hw = &genamux->mux.hw;
-
- __clk_hw_set_clk(mux_hw, hw);
-
- clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF);
-}
-
-static int clkgena_divmux_is_enabled(struct clk_hw *hw)
-{
- struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
- struct clk_hw *mux_hw = &genamux->mux.hw;
-
- __clk_hw_set_clk(mux_hw, hw);
-
- return (s8)clk_mux_ops.get_parent(mux_hw) > 0;
-}
-
-static u8 clkgena_divmux_get_parent(struct clk_hw *hw)
-{
- struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
- struct clk_hw *mux_hw = &genamux->mux.hw;
-
- __clk_hw_set_clk(mux_hw, hw);
-
- genamux->muxsel = clk_mux_ops.get_parent(mux_hw);
- if ((s8)genamux->muxsel < 0) {
- pr_debug("%s: %s: Invalid parent, setting to default.\n",
- __func__, clk_hw_get_name(hw));
- genamux->muxsel = 0;
- }
-
- return genamux->muxsel;
-}
-
-static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index)
-{
- struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
-
- if (index >= CKGAX_CLKOPSRC_SWITCH_OFF)
- return -EINVAL;
-
- genamux->muxsel = index;
-
- /*
- * If the mux is already enabled, call enable directly to set the
- * new mux position and wait for it to start running again. Otherwise
- * do nothing.
- */
- if (clkgena_divmux_is_enabled(hw))
- clkgena_divmux_enable(hw);
-
- return 0;
-}
-
-static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
- struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
-
- __clk_hw_set_clk(div_hw, hw);
-
- return clk_divider_ops.recalc_rate(div_hw, parent_rate);
-}
-
-static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
-{
- struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
- struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
-
- __clk_hw_set_clk(div_hw, hw);
-
- return clk_divider_ops.set_rate(div_hw, rate, parent_rate);
-}
-
-static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
-{
- struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
- struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
-
- __clk_hw_set_clk(div_hw, hw);
-
- return clk_divider_ops.round_rate(div_hw, rate, prate);
-}
-
-static const struct clk_ops clkgena_divmux_ops = {
- .enable = clkgena_divmux_enable,
- .disable = clkgena_divmux_disable,
- .is_enabled = clkgena_divmux_is_enabled,
- .get_parent = clkgena_divmux_get_parent,
- .set_parent = clkgena_divmux_set_parent,
- .round_rate = clkgena_divmux_round_rate,
- .recalc_rate = clkgena_divmux_recalc_rate,
- .set_rate = clkgena_divmux_set_rate,
-};
-
-/**
- * clk_register_genamux - register a genamux clock with the clock framework
- */
-static struct clk * __init clk_register_genamux(const char *name,
- const char **parent_names, u8 num_parents,
- void __iomem *reg,
- const struct clkgena_divmux_data *muxdata,
- u32 idx)
-{
- /*
- * Fixed constants across all ClockgenA variants
- */
- const int mux_width = 2;
- const int divider_width = 5;
- struct clkgena_divmux *genamux;
- struct clk *clk;
- struct clk_init_data init;
- int i;
-
- genamux = kzalloc(sizeof(*genamux), GFP_KERNEL);
- if (!genamux)
- return ERR_PTR(-ENOMEM);
-
- init.name = name;
- init.ops = &clkgena_divmux_ops;
- init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE;
- init.parent_names = parent_names;
- init.num_parents = num_parents;
-
- genamux->mux.lock = &clkgena_divmux_lock;
- genamux->mux.mask = BIT(mux_width) - 1;
- genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width);
- if (genamux->mux.shift > 31) {
- /*
- * We have spilled into the second mux register so
- * adjust the register address and the bit shift accordingly
- */
- genamux->mux.reg = reg + muxdata->mux_offset2;
- genamux->mux.shift -= 32;
- } else {
- genamux->mux.reg = reg + muxdata->mux_offset;
- }
-
- for (i = 0; i < NUM_INPUTS; i++) {
- /*
- * Divider config for each input
- */
- void __iomem *divbase = reg + muxdata->div_offsets[i];
- genamux->div[i].width = divider_width;
- genamux->div[i].reg = divbase + (idx * sizeof(u32));
-
- /*
- * Mux enabled/running feedback register for each input.
- */
- genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i];
- }
-
- genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx;
- genamux->hw.init = &init;
-
- clk = clk_register(NULL, &genamux->hw);
- if (IS_ERR(clk)) {
- kfree(genamux);
- goto err;
- }
-
- pr_debug("%s: parent %s rate %lu\n",
- __clk_get_name(clk),
- __clk_get_name(clk_get_parent(clk)),
- clk_get_rate(clk));
-err:
- return clk;
-}
-
-static struct clkgena_divmux_data st_divmux_c65hs = {
- .num_outputs = 4,
- .mux_offset = 0x14,
- .mux_start_bit = 0,
- .div_offsets = { 0x800, 0x900, 0xb00 },
- .fb_offsets = { 0x18, 0x1c, 0x20 },
- .fb_start_bit_idx = 0,
-};
-
-static struct clkgena_divmux_data st_divmux_c65ls = {
- .num_outputs = 14,
- .mux_offset = 0x14,
- .mux_offset2 = 0x24,
- .mux_start_bit = 8,
- .div_offsets = { 0x810, 0xa10, 0xb10 },
- .fb_offsets = { 0x18, 0x1c, 0x20 },
- .fb_start_bit_idx = 4,
-};
-
-static struct clkgena_divmux_data st_divmux_c32odf0 = {
- .num_outputs = 8,
- .mux_offset = 0x1c,
- .mux_start_bit = 0,
- .div_offsets = { 0x800, 0x900, 0xa60 },
- .fb_offsets = { 0x2c, 0x24, 0x28 },
- .fb_start_bit_idx = 0,
-};
-
-static struct clkgena_divmux_data st_divmux_c32odf1 = {
- .num_outputs = 8,
- .mux_offset = 0x1c,
- .mux_start_bit = 16,
- .div_offsets = { 0x820, 0x980, 0xa80 },
- .fb_offsets = { 0x2c, 0x24, 0x28 },
- .fb_start_bit_idx = 8,
-};
-
-static struct clkgena_divmux_data st_divmux_c32odf2 = {
- .num_outputs = 8,
- .mux_offset = 0x20,
- .mux_start_bit = 0,
- .div_offsets = { 0x840, 0xa20, 0xb10 },
- .fb_offsets = { 0x2c, 0x24, 0x28 },
- .fb_start_bit_idx = 16,
-};
-
-static struct clkgena_divmux_data st_divmux_c32odf3 = {
- .num_outputs = 8,
- .mux_offset = 0x20,
- .mux_start_bit = 16,
- .div_offsets = { 0x860, 0xa40, 0xb30 },
- .fb_offsets = { 0x2c, 0x24, 0x28 },
- .fb_start_bit_idx = 24,
-};
-
-static const struct of_device_id clkgena_divmux_of_match[] = {
- {
- .compatible = "st,clkgena-divmux-c65-hs",
- .data = &st_divmux_c65hs,
- },
- {
- .compatible = "st,clkgena-divmux-c65-ls",
- .data = &st_divmux_c65ls,
- },
- {
- .compatible = "st,clkgena-divmux-c32-odf0",
- .data = &st_divmux_c32odf0,
- },
- {
- .compatible = "st,clkgena-divmux-c32-odf1",
- .data = &st_divmux_c32odf1,
- },
- {
- .compatible = "st,clkgena-divmux-c32-odf2",
- .data = &st_divmux_c32odf2,
- },
- {
- .compatible = "st,clkgena-divmux-c32-odf3",
- .data = &st_divmux_c32odf3,
- },
- {}
-};
-
-static void __iomem * __init clkgen_get_register_base(struct device_node *np)
-{
- struct device_node *pnode;
- void __iomem *reg;
-
- pnode = of_get_parent(np);
- if (!pnode)
- return NULL;
-
- reg = of_iomap(pnode, 0);
-
- of_node_put(pnode);
- return reg;
-}
-
-static void __init st_of_clkgena_divmux_setup(struct device_node *np)
-{
- const struct of_device_id *match;
- const struct clkgena_divmux_data *data;
- struct clk_onecell_data *clk_data;
- void __iomem *reg;
- const char **parents;
- int num_parents = 0, i;
-
- match = of_match_node(clkgena_divmux_of_match, np);
- if (WARN_ON(!match))
- return;
-
- data = match->data;
-
- reg = clkgen_get_register_base(np);
- if (!reg)
- return;
-
- parents = clkgen_mux_get_parents(np, &num_parents);
- if (IS_ERR(parents))
- goto err_parents;
-
- clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
- if (!clk_data)
- goto err_alloc;
-
- clk_data->clk_num = data->num_outputs;
- clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *),
- GFP_KERNEL);
-
- if (!clk_data->clks)
- goto err_alloc_clks;
-
- for (i = 0; i < clk_data->clk_num; i++) {
- struct clk *clk;
- const char *clk_name;
-
- if (of_property_read_string_index(np, "clock-output-names",
- i, &clk_name))
- break;
-
- /*
- * If we read an empty clock name then the output is unused
- */
- if (*clk_name == '\0')
- continue;
-
- clk = clk_register_genamux(clk_name, parents, num_parents,
- reg, data, i);
-
- if (IS_ERR(clk))
- goto err;
-
- clk_data->clks[i] = clk;
- }
-
- kfree(parents);
-
- of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
- return;
-err:
- kfree(clk_data->clks);
-err_alloc_clks:
- kfree(clk_data);
-err_alloc:
- kfree(parents);
-err_parents:
- iounmap(reg);
-}
-CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup);
-
-struct clkgena_prediv_data {
- u32 offset;
- u8 shift;
- struct clk_div_table *table;
-};
-
-static struct clk_div_table prediv_table16[] = {
- { .val = 0, .div = 1 },
- { .val = 1, .div = 16 },
- { .div = 0 },
-};
-
-static struct clkgena_prediv_data prediv_c65_data = {
- .offset = 0x4c,
- .shift = 31,
- .table = prediv_table16,
-};
-
-static struct clkgena_prediv_data prediv_c32_data = {
- .offset = 0x50,
- .shift = 1,
- .table = prediv_table16,
-};
-
-static const struct of_device_id clkgena_prediv_of_match[] = {
- { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data },
- { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data },
- {}
-};
-
-static void __init st_of_clkgena_prediv_setup(struct device_node *np)
-{
- const struct of_device_id *match;
- void __iomem *reg;
- const char *parent_name, *clk_name;
- struct clk *clk;
- const struct clkgena_prediv_data *data;
-
- match = of_match_node(clkgena_prediv_of_match, np);
- if (!match) {
- pr_err("%s: No matching data\n", __func__);
- return;
- }
-
- data = match->data;
-
- reg = clkgen_get_register_base(np);
- if (!reg)
- return;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- goto err;
-
- if (of_property_read_string_index(np, "clock-output-names",
- 0, &clk_name))
- goto err;
-
- clk = clk_register_divider_table(NULL, clk_name, parent_name,
- CLK_GET_RATE_NOCACHE,
- reg + data->offset, data->shift, 1,
- 0, data->table, NULL);
- if (IS_ERR(clk))
- goto err;
-
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
- pr_debug("%s: parent %s rate %u\n",
- __clk_get_name(clk),
- __clk_get_name(clk_get_parent(clk)),
- (unsigned int)clk_get_rate(clk));
-
- return;
-err:
- iounmap(reg);
-}
-CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);
-
struct clkgen_mux_data {
u32 offset;
u8 shift;
@@ -541,49 +46,6 @@ struct clkgen_mux_data {
u8 mux_flags;
};
-static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = {
- .offset = 0,
- .shift = 0,
- .width = 1,
-};
-
-static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = {
- .offset = 0,
- .shift = 0,
- .width = 1,
-};
-
-static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = {
- .offset = 0,
- .shift = 0,
- .width = 1,
-};
-
-static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = {
- .offset = 0,
- .shift = 16,
- .width = 1,
- .lock = &clkgenf_lock,
-};
-
-static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = {
- .offset = 0,
- .shift = 17,
- .width = 1,
- .lock = &clkgenf_lock,
-};
-
-static struct clkgen_mux_data stih415_a9_mux_data = {
- .offset = 0,
- .shift = 1,
- .width = 2,
- .lock = &clkgen_a9_lock,
-};
-static struct clkgen_mux_data stih416_a9_mux_data = {
- .offset = 0,
- .shift = 0,
- .width = 2,
-};
static struct clkgen_mux_data stih407_a9_mux_data = {
.offset = 0x1a4,
.shift = 0,
@@ -591,58 +53,13 @@ static struct clkgen_mux_data stih407_a9_mux_data = {
.lock = &clkgen_a9_lock,
};
-static const struct of_device_id mux_of_match[] = {
- {
- .compatible = "st,stih416-clkgenc-vcc-hd",
- .data = &clkgen_mux_c_vcc_hd_416,
- },
- {
- .compatible = "st,stih416-clkgenf-vcc-fvdp",
- .data = &clkgen_mux_f_vcc_fvdp_416,
- },
- {
- .compatible = "st,stih416-clkgenf-vcc-hva",
- .data = &clkgen_mux_f_vcc_hva_416,
- },
- {
- .compatible = "st,stih416-clkgenf-vcc-hd",
- .data = &clkgen_mux_f_vcc_hd_416,
- },
- {
- .compatible = "st,stih416-clkgenf-vcc-sd",
- .data = &clkgen_mux_c_vcc_sd_416,
- },
- {
- .compatible = "st,stih415-clkgen-a9-mux",
- .data = &stih415_a9_mux_data,
- },
- {
- .compatible = "st,stih416-clkgen-a9-mux",
- .data = &stih416_a9_mux_data,
- },
- {
- .compatible = "st,stih407-clkgen-a9-mux",
- .data = &stih407_a9_mux_data,
- },
- {}
-};
-
-static void __init st_of_clkgen_mux_setup(struct device_node *np)
+static void __init st_of_clkgen_mux_setup(struct device_node *np,
+ struct clkgen_mux_data *data)
{
- const struct of_device_id *match;
struct clk *clk;
void __iomem *reg;
const char **parents;
- int num_parents;
- const struct clkgen_mux_data *data;
-
- match = of_match_node(mux_of_match, np);
- if (!match) {
- pr_err("%s: No matching data\n", __func__);
- return;
- }
-
- data = match->data;
+ int num_parents = 0;
reg = of_iomap(np, 0);
if (!reg) {
@@ -679,161 +96,10 @@ err:
err_parents:
iounmap(reg);
}
-CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup);
-
-#define VCC_MAX_CHANNELS 16
-
-#define VCC_GATE_OFFSET 0x0
-#define VCC_MUX_OFFSET 0x4
-#define VCC_DIV_OFFSET 0x8
-
-struct clkgen_vcc_data {
- spinlock_t *lock;
- unsigned long clk_flags;
-};
-
-static struct clkgen_vcc_data st_clkgenc_vcc_416 = {
- .clk_flags = CLK_SET_RATE_PARENT,
-};
-
-static struct clkgen_vcc_data st_clkgenf_vcc_416 = {
- .lock = &clkgenf_lock,
-};
-
-static const struct of_device_id vcc_of_match[] = {
- { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 },
- { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 },
- {}
-};
-static void __init st_of_clkgen_vcc_setup(struct device_node *np)
+static void __init st_of_clkgen_a9_mux_setup(struct device_node *np)
{
- const struct of_device_id *match;
- void __iomem *reg;
- const char **parents;
- int num_parents, i;
- struct clk_onecell_data *clk_data;
- const struct clkgen_vcc_data *data;
-
- match = of_match_node(vcc_of_match, np);
- if (WARN_ON(!match))
- return;
- data = match->data;
-
- reg = of_iomap(np, 0);
- if (!reg)
- return;
-
- parents = clkgen_mux_get_parents(np, &num_parents);
- if (IS_ERR(parents))
- goto err_parents;
-
- clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
- if (!clk_data)
- goto err_alloc;
-
- clk_data->clk_num = VCC_MAX_CHANNELS;
- clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *),
- GFP_KERNEL);
-
- if (!clk_data->clks)
- goto err_alloc_clks;
-
- for (i = 0; i < clk_data->clk_num; i++) {
- struct clk *clk;
- const char *clk_name;
- struct clk_gate *gate;
- struct clk_divider *div;
- struct clk_mux *mux;
-
- if (of_property_read_string_index(np, "clock-output-names",
- i, &clk_name))
- break;
-
- /*
- * If we read an empty clock name then the output is unused
- */
- if (*clk_name == '\0')
- continue;
-
- gate = kzalloc(sizeof(*gate), GFP_KERNEL);
- if (!gate)
- goto err;
-
- div = kzalloc(sizeof(*div), GFP_KERNEL);
- if (!div) {
- kfree(gate);
- goto err;
- }
-
- mux = kzalloc(sizeof(*mux), GFP_KERNEL);
- if (!mux) {
- kfree(gate);
- kfree(div);
- goto err;
- }
-
- gate->reg = reg + VCC_GATE_OFFSET;
- gate->bit_idx = i;
- gate->flags = CLK_GATE_SET_TO_DISABLE;
- gate->lock = data->lock;
-
- div->reg = reg + VCC_DIV_OFFSET;
- div->shift = 2 * i;
- div->width = 2;
- div->flags = CLK_DIVIDER_POWER_OF_TWO |
- CLK_DIVIDER_ROUND_CLOSEST;
-
- mux->reg = reg + VCC_MUX_OFFSET;
- mux->shift = 2 * i;
- mux->mask = 0x3;
-
- clk = clk_register_composite(NULL, clk_name, parents,
- num_parents,
- &mux->hw, &clk_mux_ops,
- &div->hw, &clk_divider_ops,
- &gate->hw, &clk_gate_ops,
- data->clk_flags |
- CLK_GET_RATE_NOCACHE);
- if (IS_ERR(clk)) {
- kfree(gate);
- kfree(div);
- kfree(mux);
- goto err;
- }
-
- pr_debug("%s: parent %s rate %u\n",
- __clk_get_name(clk),
- __clk_get_name(clk_get_parent(clk)),
- (unsigned int)clk_get_rate(clk));
-
- clk_data->clks[i] = clk;
- }
-
- kfree(parents);
-
- of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
- return;
-
-err:
- for (i = 0; i < clk_data->clk_num; i++) {
- struct clk_composite *composite;
-
- if (!clk_data->clks[i])
- continue;
-
- composite = to_clk_composite(__clk_get_hw(clk_data->clks[i]));
- kfree(to_clk_gate(composite->gate_hw));
- kfree(to_clk_divider(composite->rate_hw));
- kfree(to_clk_mux(composite->mux_hw));
- }
-
- kfree(clk_data->clks);
-err_alloc_clks:
- kfree(clk_data);
-err_alloc:
- kfree(parents);
-err_parents:
- iounmap(reg);
+ st_of_clkgen_mux_setup(np, &stih407_a9_mux_data);
}
-CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup);
+CLK_OF_DECLARE(clkgen_a9mux, "st,stih407-clkgen-a9-mux",
+ st_of_clkgen_a9_mux_setup);
diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c
index 0b5990e..25bda48 100644
--- a/drivers/clk/st/clkgen-pll.c
+++ b/drivers/clk/st/clkgen-pll.c
@@ -26,14 +26,6 @@ static DEFINE_SPINLOCK(clkgena_c32_odf_lock);
DEFINE_SPINLOCK(clkgen_a9_lock);
/*
- * Common PLL configuration register bits for PLL800 and PLL1600 C65
- */
-#define C65_MDIV_PLL800_MASK (0xff)
-#define C65_MDIV_PLL1600_MASK (0x7)
-#define C65_NDIV_MASK (0xff)
-#define C65_PDIV_MASK (0x7)
-
-/*
* PLL configuration register bits for PLL3200 C32
*/
#define C32_NDIV_MASK (0xff)
@@ -70,144 +62,10 @@ struct clkgen_pll_data {
const struct clk_ops *ops;
};
-static const struct clk_ops st_pll1600c65_ops;
-static const struct clk_ops st_pll800c65_ops;
static const struct clk_ops stm_pll3200c32_ops;
static const struct clk_ops stm_pll3200c32_a9_ops;
-static const struct clk_ops st_pll1200c32_ops;
static const struct clk_ops stm_pll4600c28_ops;
-static const struct clkgen_pll_data st_pll1600c65_ax = {
- .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19),
- .pdn_ctrl = CLKGEN_FIELD(0x10, 0x1, 0),
- .locked_status = CLKGEN_FIELD(0x0, 0x1, 31),
- .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL1600_MASK, 0),
- .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8),
- .ops = &st_pll1600c65_ops
-};
-
-static const struct clkgen_pll_data st_pll800c65_ax = {
- .pdn_status = CLKGEN_FIELD(0x0, 0x1, 19),
- .pdn_ctrl = CLKGEN_FIELD(0xC, 0x1, 1),
- .locked_status = CLKGEN_FIELD(0x0, 0x1, 31),
- .mdiv = CLKGEN_FIELD(0x0, C65_MDIV_PLL800_MASK, 0),
- .ndiv = CLKGEN_FIELD(0x0, C65_NDIV_MASK, 8),
- .pdiv = CLKGEN_FIELD(0x0, C65_PDIV_MASK, 16),
- .ops = &st_pll800c65_ops
-};
-
-static const struct clkgen_pll_data st_pll3200c32_a1x_0 = {
- .pdn_status = CLKGEN_FIELD(0x0, 0x1, 31),
- .pdn_ctrl = CLKGEN_FIELD(0x18, 0x1, 0),
- .locked_status = CLKGEN_FIELD(0x4, 0x1, 31),
- .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 0x0),
- .idf = CLKGEN_FIELD(0x4, C32_IDF_MASK, 0x0),
- .num_odfs = 4,
- .odf = { CLKGEN_FIELD(0x54, C32_ODF_MASK, 4),
- CLKGEN_FIELD(0x54, C32_ODF_MASK, 10),
- CLKGEN_FIELD(0x54, C32_ODF_MASK, 16),
- CLKGEN_FIELD(0x54, C32_ODF_MASK, 22) },
- .odf_gate = { CLKGEN_FIELD(0x54, 0x1, 0),
- CLKGEN_FIELD(0x54, 0x1, 1),
- CLKGEN_FIELD(0x54, 0x1, 2),
- CLKGEN_FIELD(0x54, 0x1, 3) },
- .ops = &stm_pll3200c32_ops,
-};
-
-static const struct clkgen_pll_data st_pll3200c32_a1x_1 = {
- .pdn_status = CLKGEN_FIELD(0xC, 0x1, 31),
- .pdn_ctrl = CLKGEN_FIELD(0x18, 0x1, 1),
- .locked_status = CLKGEN_FIELD(0x10, 0x1, 31),
- .ndiv = CLKGEN_FIELD(0xC, C32_NDIV_MASK, 0x0),
- .idf = CLKGEN_FIELD(0x10, C32_IDF_MASK, 0x0),
- .num_odfs = 4,
- .odf = { CLKGEN_FIELD(0x58, C32_ODF_MASK, 4),
- CLKGEN_FIELD(0x58, C32_ODF_MASK, 10),
- CLKGEN_FIELD(0x58, C32_ODF_MASK, 16),
- CLKGEN_FIELD(0x58, C32_ODF_MASK, 22) },
- .odf_gate = { CLKGEN_FIELD(0x58, 0x1, 0),
- CLKGEN_FIELD(0x58, 0x1, 1),
- CLKGEN_FIELD(0x58, 0x1, 2),
- CLKGEN_FIELD(0x58, 0x1, 3) },
- .ops = &stm_pll3200c32_ops,
-};
-
-/* 415 specific */
-static const struct clkgen_pll_data st_pll3200c32_a9_415 = {
- .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
- .pdn_ctrl = CLKGEN_FIELD(0x0, 0x1, 0),
- .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0),
- .ndiv = CLKGEN_FIELD(0x0, C32_NDIV_MASK, 9),
- .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 22),
- .num_odfs = 1,
- .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 3) },
- .odf_gate = { CLKGEN_FIELD(0x0, 0x1, 28) },
- .ops = &stm_pll3200c32_ops,
-};
-
-static const struct clkgen_pll_data st_pll3200c32_ddr_415 = {
- .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
- .pdn_ctrl = CLKGEN_FIELD(0x0, 0x1, 0),
- .locked_status = CLKGEN_FIELD(0x100, 0x1, 0),
- .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
- .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
- .num_odfs = 2,
- .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8),
- CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) },
- .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28),
- CLKGEN_FIELD(0x4, 0x1, 29) },
- .ops = &stm_pll3200c32_ops,
-};
-
-static const struct clkgen_pll_data st_pll1200c32_gpu_415 = {
- .pdn_status = CLKGEN_FIELD(0x4, 0x1, 0),
- .pdn_ctrl = CLKGEN_FIELD(0x4, 0x1, 0),
- .locked_status = CLKGEN_FIELD(0x168, 0x1, 0),
- .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3),
- .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0),
- .num_odfs = 0,
- .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) },
- .ops = &st_pll1200c32_ops,
-};
-
-/* 416 specific */
-static const struct clkgen_pll_data st_pll3200c32_a9_416 = {
- .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
- .pdn_ctrl = CLKGEN_FIELD(0x0, 0x1, 0),
- .locked_status = CLKGEN_FIELD(0x6C, 0x1, 0),
- .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
- .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
- .num_odfs = 1,
- .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8) },
- .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28) },
- .ops = &stm_pll3200c32_ops,
-};
-
-static const struct clkgen_pll_data st_pll3200c32_ddr_416 = {
- .pdn_status = CLKGEN_FIELD(0x0, 0x1, 0),
- .pdn_ctrl = CLKGEN_FIELD(0x0, 0x1, 0),
- .locked_status = CLKGEN_FIELD(0x10C, 0x1, 0),
- .ndiv = CLKGEN_FIELD(0x8, C32_NDIV_MASK, 0),
- .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 25),
- .num_odfs = 2,
- .odf = { CLKGEN_FIELD(0x8, C32_ODF_MASK, 8),
- CLKGEN_FIELD(0x8, C32_ODF_MASK, 14) },
- .odf_gate = { CLKGEN_FIELD(0x4, 0x1, 28),
- CLKGEN_FIELD(0x4, 0x1, 29) },
- .ops = &stm_pll3200c32_ops,
-};
-
-static const struct clkgen_pll_data st_pll1200c32_gpu_416 = {
- .pdn_status = CLKGEN_FIELD(0x8E4, 0x1, 3),
- .pdn_ctrl = CLKGEN_FIELD(0x8E4, 0x1, 3),
- .locked_status = CLKGEN_FIELD(0x90C, 0x1, 0),
- .ldf = CLKGEN_FIELD(0x0, C32_LDF_MASK, 3),
- .idf = CLKGEN_FIELD(0x0, C32_IDF_MASK, 0),
- .num_odfs = 0,
- .odf = { CLKGEN_FIELD(0x0, C32_ODF_MASK, 10) },
- .ops = &st_pll1200c32_ops,
-};
-
static const struct clkgen_pll_data st_pll3200c32_407_a0 = {
/* 407 A0 */
.pdn_status = CLKGEN_FIELD(0x2a0, 0x1, 8),
@@ -410,57 +268,6 @@ static void clkgen_pll_disable(struct clk_hw *hw)
spin_unlock_irqrestore(pll->lock, flags);
}
-static unsigned long recalc_stm_pll800c65(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct clkgen_pll *pll = to_clkgen_pll(hw);
- unsigned long mdiv, ndiv, pdiv;
- unsigned long rate;
- uint64_t res;
-
- if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
- return 0;
-
- pdiv = CLKGEN_READ(pll, pdiv);
- mdiv = CLKGEN_READ(pll, mdiv);
- ndiv = CLKGEN_READ(pll, ndiv);
-
- if (!mdiv)
- mdiv++; /* mdiv=0 or 1 => MDIV=1 */
-
- res = (uint64_t)2 * (uint64_t)parent_rate * (uint64_t)ndiv;
- rate = (unsigned long)div64_u64(res, mdiv * (1 << pdiv));
-
- pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate);
-
- return rate;
-
-}
-
-static unsigned long recalc_stm_pll1600c65(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct clkgen_pll *pll = to_clkgen_pll(hw);
- unsigned long mdiv, ndiv;
- unsigned long rate;
-
- if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
- return 0;
-
- mdiv = CLKGEN_READ(pll, mdiv);
- ndiv = CLKGEN_READ(pll, ndiv);
-
- if (!mdiv)
- mdiv = 1;
-
- /* Note: input is divided by 1000 to avoid overflow */
- rate = ((2 * (parent_rate / 1000) * ndiv) / mdiv) * 1000;
-
- pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate);
-
- return rate;
-}
-
static int clk_pll3200c32_get_params(unsigned long input, unsigned long output,
struct stm_pll *pll)
{
@@ -608,33 +415,6 @@ static int set_rate_stm_pll3200c32(struct clk_hw *hw, unsigned long rate,
return 0;
}
-static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct clkgen_pll *pll = to_clkgen_pll(hw);
- unsigned long odf, ldf, idf;
- unsigned long rate;
-
- if (!clkgen_pll_is_enabled(hw) || !clkgen_pll_is_locked(hw))
- return 0;
-
- odf = CLKGEN_READ(pll, odf[0]);
- ldf = CLKGEN_READ(pll, ldf);
- idf = CLKGEN_READ(pll, idf);
-
- if (!idf) /* idf==0 means 1 */
- idf = 1;
- if (!odf) /* odf==0 means 1 */
- odf = 1;
-
- /* Note: input is divided by 1000 to avoid overflow */
- rate = (((parent_rate / 1000) * ldf) / (odf * idf)) * 1000;
-
- pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate);
-
- return rate;
-}
-
/* PLL output structure
* FVCO >> /2 >> FVCOBY2 (no output)
* |> Divider (ODF) >> PHI
@@ -792,20 +572,6 @@ static int set_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate,
return 0;
}
-static const struct clk_ops st_pll1600c65_ops = {
- .enable = clkgen_pll_enable,
- .disable = clkgen_pll_disable,
- .is_enabled = clkgen_pll_is_enabled,
- .recalc_rate = recalc_stm_pll1600c65,
-};
-
-static const struct clk_ops st_pll800c65_ops = {
- .enable = clkgen_pll_enable,
- .disable = clkgen_pll_disable,
- .is_enabled = clkgen_pll_is_enabled,
- .recalc_rate = recalc_stm_pll800c65,
-};
-
static const struct clk_ops stm_pll3200c32_ops = {
.enable = clkgen_pll_enable,
.disable = clkgen_pll_disable,
@@ -822,13 +588,6 @@ static const struct clk_ops stm_pll3200c32_a9_ops = {
.set_rate = set_rate_stm_pll3200c32,
};
-static const struct clk_ops st_pll1200c32_ops = {
- .enable = clkgen_pll_enable,
- .disable = clkgen_pll_disable,
- .is_enabled = clkgen_pll_is_enabled,
- .recalc_rate = recalc_stm_pll1200c32,
-};
-
static const struct clk_ops stm_pll4600c28_ops = {
.enable = clkgen_pll_enable,
.disable = clkgen_pll_disable,
@@ -877,22 +636,6 @@ static struct clk * __init clkgen_pll_register(const char *parent_name,
return clk;
}
-static struct clk * __init clkgen_c65_lsdiv_register(const char *parent_name,
- const char *clk_name)
-{
- struct clk *clk;
-
- clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, 1, 2);
- if (IS_ERR(clk))
- return clk;
-
- pr_debug("%s: parent %s rate %lu\n",
- __clk_get_name(clk),
- __clk_get_name(clk_get_parent(clk)),
- clk_get_rate(clk));
- return clk;
-}
-
static void __iomem * __init clkgen_get_register_base(
struct device_node *np)
{
@@ -909,89 +652,6 @@ static void __iomem * __init clkgen_get_register_base(
return reg;
}
-#define CLKGENAx_PLL0_OFFSET 0x0
-#define CLKGENAx_PLL1_OFFSET 0x4
-
-static void __init clkgena_c65_pll_setup(struct device_node *np)
-{
- const int num_pll_outputs = 3;
- struct clk_onecell_data *clk_data;
- const char *parent_name;
- void __iomem *reg;
- const char *clk_name;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- return;
-
- reg = clkgen_get_register_base(np);
- if (!reg)
- return;
-
- clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
- if (!clk_data)
- return;
-
- clk_data->clk_num = num_pll_outputs;
- clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
- GFP_KERNEL);
-
- if (!clk_data->clks)
- goto err;
-
- if (of_property_read_string_index(np, "clock-output-names",
- 0, &clk_name))
- goto err;
-
- /*
- * PLL0 HS (high speed) output
- */
- clk_data->clks[0] = clkgen_pll_register(parent_name,
- (struct clkgen_pll_data *) &st_pll1600c65_ax,
- reg + CLKGENAx_PLL0_OFFSET, 0, clk_name, NULL);
-
- if (IS_ERR(clk_data->clks[0]))
- goto err;
-
- if (of_property_read_string_index(np, "clock-output-names",
- 1, &clk_name))
- goto err;
-
- /*
- * PLL0 LS (low speed) output, which is a fixed divide by 2 of the
- * high speed output.
- */
- clk_data->clks[1] = clkgen_c65_lsdiv_register(__clk_get_name
- (clk_data->clks[0]),
- clk_name);
-
- if (IS_ERR(clk_data->clks[1]))
- goto err;
-
- if (of_property_read_string_index(np, "clock-output-names",
- 2, &clk_name))
- goto err;
-
- /*
- * PLL1 output
- */
- clk_data->clks[2] = clkgen_pll_register(parent_name,
- (struct clkgen_pll_data *) &st_pll800c65_ax,
- reg + CLKGENAx_PLL1_OFFSET, 0, clk_name, NULL);
-
- if (IS_ERR(clk_data->clks[2]))
- goto err;
-
- of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
- return;
-
-err:
- kfree(clk_data->clks);
- kfree(clk_data);
-}
-CLK_OF_DECLARE(clkgena_c65_plls,
- "st,clkgena-plls-c65", clkgena_c65_pll_setup);
-
static struct clk * __init clkgen_odf_register(const char *parent_name,
void __iomem *reg,
struct clkgen_pll_data *pll_data,
@@ -1042,72 +702,17 @@ static struct clk * __init clkgen_odf_register(const char *parent_name,
return clk;
}
-static const struct of_device_id c32_pll_of_match[] = {
- {
- .compatible = "st,plls-c32-a1x-0",
- .data = &st_pll3200c32_a1x_0,
- },
- {
- .compatible = "st,plls-c32-a1x-1",
- .data = &st_pll3200c32_a1x_1,
- },
- {
- .compatible = "st,stih415-plls-c32-a9",
- .data = &st_pll3200c32_a9_415,
- },
- {
- .compatible = "st,stih415-plls-c32-ddr",
- .data = &st_pll3200c32_ddr_415,
- },
- {
- .compatible = "st,stih416-plls-c32-a9",
- .data = &st_pll3200c32_a9_416,
- },
- {
- .compatible = "st,stih416-plls-c32-ddr",
- .data = &st_pll3200c32_ddr_416,
- },
- {
- .compatible = "st,stih407-plls-c32-a0",
- .data = &st_pll3200c32_407_a0,
- },
- {
- .compatible = "st,plls-c32-cx_0",
- .data = &st_pll3200c32_cx_0,
- },
- {
- .compatible = "st,plls-c32-cx_1",
- .data = &st_pll3200c32_cx_1,
- },
- {
- .compatible = "st,stih407-plls-c32-a9",
- .data = &st_pll3200c32_407_a9,
- },
- {
- .compatible = "st,stih418-plls-c28-a9",
- .data = &st_pll4600c28_418_a9,
- },
- {}
-};
-static void __init clkgen_c32_pll_setup(struct device_node *np)
+static void __init clkgen_c32_pll_setup(struct device_node *np,
+ struct clkgen_pll_data *data)
{
- const struct of_device_id *match;
struct clk *clk;
const char *parent_name, *pll_name;
void __iomem *pll_base;
int num_odfs, odf;
struct clk_onecell_data *clk_data;
- struct clkgen_pll_data *data;
unsigned long pll_flags = 0;
- match = of_match_node(c32_pll_of_match, np);
- if (!match) {
- pr_err("%s: No matching data\n", __func__);
- return;
- }
-
- data = (struct clkgen_pll_data *) match->data;
parent_name = of_clk_get_parent_name(np, 0);
if (!parent_name)
@@ -1166,59 +771,30 @@ err:
kfree(clk_data->clks);
kfree(clk_data);
}
-CLK_OF_DECLARE(clkgen_c32_pll, "st,clkgen-plls-c32", clkgen_c32_pll_setup);
-
-static const struct of_device_id c32_gpu_pll_of_match[] = {
- {
- .compatible = "st,stih415-gpu-pll-c32",
- .data = &st_pll1200c32_gpu_415,
- },
- {
- .compatible = "st,stih416-gpu-pll-c32",
- .data = &st_pll1200c32_gpu_416,
- },
- {}
-};
-
-static void __init clkgengpu_c32_pll_setup(struct device_node *np)
+static void __init clkgen_c32_pll0_setup(struct device_node *np)
{
- const struct of_device_id *match;
- struct clk *clk;
- const char *parent_name;
- void __iomem *reg;
- const char *clk_name;
- struct clkgen_pll_data *data;
-
- match = of_match_node(c32_gpu_pll_of_match, np);
- if (!match) {
- pr_err("%s: No matching data\n", __func__);
- return;
- }
-
- data = (struct clkgen_pll_data *)match->data;
-
- parent_name = of_clk_get_parent_name(np, 0);
- if (!parent_name)
- return;
-
- reg = clkgen_get_register_base(np);
- if (!reg)
- return;
-
- if (of_property_read_string_index(np, "clock-output-names",
- 0, &clk_name))
- return;
+ clkgen_c32_pll_setup(np,
+ (struct clkgen_pll_data *) &st_pll3200c32_cx_0);
+}
+CLK_OF_DECLARE(c32_pll0, "st,clkgen-pll0", clkgen_c32_pll0_setup);
- /*
- * PLL 1200MHz output
- */
- clk = clkgen_pll_register(parent_name, data, reg,
- 0, clk_name, data->lock);
+static void __init clkgen_c32_pll1_setup(struct device_node *np)
+{
+ clkgen_c32_pll_setup(np,
+ (struct clkgen_pll_data *) &st_pll3200c32_cx_1);
+}
+CLK_OF_DECLARE(c32_pll1, "st,clkgen-pll1", clkgen_c32_pll1_setup);
- if (!IS_ERR(clk))
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+static void __init clkgen_c32_plla9_setup(struct device_node *np)
+{
+ clkgen_c32_pll_setup(np,
+ (struct clkgen_pll_data *) &st_pll3200c32_407_a9);
+}
+CLK_OF_DECLARE(c32_plla9, "st,stih407-clkgen-plla9", clkgen_c32_plla9_setup);
- return;
+static void __init clkgen_c28_plla9_setup(struct device_node *np)
+{
+ clkgen_c32_pll_setup(np,
+ (struct clkgen_pll_data *) &st_pll4600c28_418_a9);
}
-CLK_OF_DECLARE(clkgengpu_c32_pll,
- "st,clkgengpu-pll-c32", clkgengpu_c32_pll_setup);
+CLK_OF_DECLARE(c28_plla9, "st,stih418-clkgen-plla9", clkgen_c28_plla9_setup);
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 2afcbd3..254d952 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -1,5 +1,6 @@
config SUNXI_CCU
bool "Clock support for Allwinner SoCs"
+ depends on ARCH_SUNXI || COMPILE_TEST
default ARCH_SUNXI
if SUNXI_CCU
@@ -19,6 +20,10 @@ config SUNXI_CCU_GATE
config SUNXI_CCU_MUX
bool
+config SUNXI_CCU_MULT
+ bool
+ select SUNXI_CCU_MUX
+
config SUNXI_CCU_PHASE
bool
@@ -51,6 +56,40 @@ config SUNXI_CCU_MP
# SoC Drivers
+config SUN6I_A31_CCU
+ bool "Support for the Allwinner A31/A31s CCU"
+ select SUNXI_CCU_DIV
+ select SUNXI_CCU_NK
+ select SUNXI_CCU_NKM
+ select SUNXI_CCU_NM
+ select SUNXI_CCU_MP
+ select SUNXI_CCU_PHASE
+ default MACH_SUN6I
+
+config SUN8I_A23_CCU
+ bool "Support for the Allwinner A23 CCU"
+ select SUNXI_CCU_DIV
+ select SUNXI_CCU_MULT
+ select SUNXI_CCU_NK
+ select SUNXI_CCU_NKM
+ select SUNXI_CCU_NKMP
+ select SUNXI_CCU_NM
+ select SUNXI_CCU_MP
+ select SUNXI_CCU_PHASE
+ default MACH_SUN8I
+
+config SUN8I_A33_CCU
+ bool "Support for the Allwinner A33 CCU"
+ select SUNXI_CCU_DIV
+ select SUNXI_CCU_MULT
+ select SUNXI_CCU_NK
+ select SUNXI_CCU_NKM
+ select SUNXI_CCU_NKMP
+ select SUNXI_CCU_NM
+ select SUNXI_CCU_MP
+ select SUNXI_CCU_PHASE
+ default MACH_SUN8I
+
config SUN8I_H3_CCU
bool "Support for the Allwinner H3 CCU"
select SUNXI_CCU_DIV
diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
index 633ce64..106cba2 100644
--- a/drivers/clk/sunxi-ng/Makefile
+++ b/drivers/clk/sunxi-ng/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_SUNXI_CCU_DIV) += ccu_div.o
obj-$(CONFIG_SUNXI_CCU_FRAC) += ccu_frac.o
obj-$(CONFIG_SUNXI_CCU_GATE) += ccu_gate.o
obj-$(CONFIG_SUNXI_CCU_MUX) += ccu_mux.o
+obj-$(CONFIG_SUNXI_CCU_MULT) += ccu_mult.o
obj-$(CONFIG_SUNXI_CCU_PHASE) += ccu_phase.o
# Multi-factor clocks
@@ -17,4 +18,7 @@ obj-$(CONFIG_SUNXI_CCU_NM) += ccu_nm.o
obj-$(CONFIG_SUNXI_CCU_MP) += ccu_mp.o
# SoC support
+obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o
+obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o
+obj-$(CONFIG_SUN8I_A33_CCU) += ccu-sun8i-a33.o
obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
new file mode 100644
index 0000000..7959646
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (c) 2016 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * Based on ccu-sun8i-h3.c by Maxime Ripard.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_mult.h"
+#include "ccu_mux.h"
+#include "ccu_nk.h"
+#include "ccu_nkm.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+#include "ccu_phase.h"
+
+#include "ccu-sun6i-a31.h"
+
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_cpu_clk, "pll-cpu",
+ "osc24M", 0x000,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ 0, 2, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+/*
+ * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from
+ * the base (2x, 4x and 8x), and one variable divider (the one true
+ * pll audio).
+ *
+ * We don't have any need for the variable divider for now, so we just
+ * hardcode it to match with the clock names
+ */
+#define SUN6I_A31_PLL_AUDIO_REG 0x008
+
+static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base",
+ "osc24M", 0x008,
+ 8, 7, /* N */
+ 0, 5, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0",
+ "osc24M", 0x010,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve",
+ "osc24M", 0x018,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr",
+ "osc24M", 0x020,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ 0, 2, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph_clk, "pll-periph",
+ "osc24M", 0x028,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 2, /* post-div */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video1_clk, "pll-video1",
+ "osc24M", 0x030,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu",
+ "osc24M", 0x038,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+/*
+ * The MIPI PLL has 2 modes: "MIPI" and "HDMI".
+ *
+ * The MIPI mode is a standard NKM-style clock. The HDMI mode is an
+ * integer / fractional clock with switchable multipliers and dividers.
+ * This is not supported here. We hardcode the PLL to MIPI mode.
+ */
+#define SUN6I_A31_PLL_MIPI_REG 0x040
+
+static const char * const pll_mipi_parents[] = { "pll-video0", "pll-video1" };
+static SUNXI_CCU_NKM_WITH_MUX_GATE_LOCK(pll_mipi_clk, "pll-mipi",
+ pll_mipi_parents, 0x040,
+ 8, 4, /* N */
+ 4, 2, /* K */
+ 0, 4, /* M */
+ 21, 0, /* mux */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll9_clk, "pll9",
+ "osc24M", 0x044,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll10_clk, "pll10",
+ "osc24M", 0x048,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static const char * const cpux_parents[] = { "osc32k", "osc24M",
+ "pll-cpu", "pll-cpu" };
+static SUNXI_CCU_MUX(cpu_clk, "cpu", cpux_parents,
+ 0x050, 16, 2, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL);
+
+static struct clk_div_table axi_div_table[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 3, .div = 4 },
+ { .val = 4, .div = 4 },
+ { .val = 5, .div = 4 },
+ { .val = 6, .div = 4 },
+ { .val = 7, .div = 4 },
+ { /* Sentinel */ },
+};
+
+static SUNXI_CCU_DIV_TABLE(axi_clk, "axi", "cpu",
+ 0x050, 0, 3, axi_div_table, 0);
+
+static const char * const ahb1_parents[] = { "osc32k", "osc24M",
+ "axi", "pll-periph" };
+
+static struct ccu_div ahb1_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO),
+
+ .mux = {
+ .shift = 12,
+ .width = 2,
+
+ .variable_prediv = {
+ .index = 3,
+ .shift = 6,
+ .width = 2,
+ },
+ },
+
+ .common = {
+ .reg = 0x054,
+ .features = CCU_FEATURE_VARIABLE_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb1",
+ ahb1_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct clk_div_table apb1_div_table[] = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 8 },
+ { /* Sentinel */ },
+};
+
+static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1",
+ 0x054, 8, 2, apb1_div_table, 0);
+
+static const char * const apb2_parents[] = { "osc32k", "osc24M",
+ "pll-periph", "pll-periph" };
+static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static SUNXI_CCU_GATE(ahb1_mipidsi_clk, "ahb1-mipidsi", "ahb1",
+ 0x060, BIT(1), 0);
+static SUNXI_CCU_GATE(ahb1_ss_clk, "ahb1-ss", "ahb1",
+ 0x060, BIT(5), 0);
+static SUNXI_CCU_GATE(ahb1_dma_clk, "ahb1-dma", "ahb1",
+ 0x060, BIT(6), 0);
+static SUNXI_CCU_GATE(ahb1_mmc0_clk, "ahb1-mmc0", "ahb1",
+ 0x060, BIT(8), 0);
+static SUNXI_CCU_GATE(ahb1_mmc1_clk, "ahb1-mmc1", "ahb1",
+ 0x060, BIT(9), 0);
+static SUNXI_CCU_GATE(ahb1_mmc2_clk, "ahb1-mmc2", "ahb1",
+ 0x060, BIT(10), 0);
+static SUNXI_CCU_GATE(ahb1_mmc3_clk, "ahb1-mmc3", "ahb1",
+ 0x060, BIT(12), 0);
+static SUNXI_CCU_GATE(ahb1_nand1_clk, "ahb1-nand1", "ahb1",
+ 0x060, BIT(13), 0);
+static SUNXI_CCU_GATE(ahb1_nand0_clk, "ahb1-nand0", "ahb1",
+ 0x060, BIT(13), 0);
+static SUNXI_CCU_GATE(ahb1_sdram_clk, "ahb1-sdram", "ahb1",
+ 0x060, BIT(14), 0);
+static SUNXI_CCU_GATE(ahb1_emac_clk, "ahb1-emac", "ahb1",
+ 0x060, BIT(17), 0);
+static SUNXI_CCU_GATE(ahb1_ts_clk, "ahb1-ts", "ahb1",
+ 0x060, BIT(18), 0);
+static SUNXI_CCU_GATE(ahb1_hstimer_clk, "ahb1-hstimer", "ahb1",
+ 0x060, BIT(19), 0);
+static SUNXI_CCU_GATE(ahb1_spi0_clk, "ahb1-spi0", "ahb1",
+ 0x060, BIT(20), 0);
+static SUNXI_CCU_GATE(ahb1_spi1_clk, "ahb1-spi1", "ahb1",
+ 0x060, BIT(21), 0);
+static SUNXI_CCU_GATE(ahb1_spi2_clk, "ahb1-spi2", "ahb1",
+ 0x060, BIT(22), 0);
+static SUNXI_CCU_GATE(ahb1_spi3_clk, "ahb1-spi3", "ahb1",
+ 0x060, BIT(23), 0);
+static SUNXI_CCU_GATE(ahb1_otg_clk, "ahb1-otg", "ahb1",
+ 0x060, BIT(24), 0);
+static SUNXI_CCU_GATE(ahb1_ehci0_clk, "ahb1-ehci0", "ahb1",
+ 0x060, BIT(26), 0);
+static SUNXI_CCU_GATE(ahb1_ehci1_clk, "ahb1-ehci1", "ahb1",
+ 0x060, BIT(27), 0);
+static SUNXI_CCU_GATE(ahb1_ohci0_clk, "ahb1-ohci0", "ahb1",
+ 0x060, BIT(29), 0);
+static SUNXI_CCU_GATE(ahb1_ohci1_clk, "ahb1-ohci1", "ahb1",
+ 0x060, BIT(30), 0);
+static SUNXI_CCU_GATE(ahb1_ohci2_clk, "ahb1-ohci2", "ahb1",
+ 0x060, BIT(31), 0);
+
+static SUNXI_CCU_GATE(ahb1_ve_clk, "ahb1-ve", "ahb1",
+ 0x064, BIT(0), 0);
+static SUNXI_CCU_GATE(ahb1_lcd0_clk, "ahb1-lcd0", "ahb1",
+ 0x064, BIT(4), 0);
+static SUNXI_CCU_GATE(ahb1_lcd1_clk, "ahb1-lcd1", "ahb1",
+ 0x064, BIT(5), 0);
+static SUNXI_CCU_GATE(ahb1_csi_clk, "ahb1-csi", "ahb1",
+ 0x064, BIT(8), 0);
+static SUNXI_CCU_GATE(ahb1_hdmi_clk, "ahb1-hdmi", "ahb1",
+ 0x064, BIT(11), 0);
+static SUNXI_CCU_GATE(ahb1_be0_clk, "ahb1-be0", "ahb1",
+ 0x064, BIT(12), 0);
+static SUNXI_CCU_GATE(ahb1_be1_clk, "ahb1-be1", "ahb1",
+ 0x064, BIT(13), 0);
+static SUNXI_CCU_GATE(ahb1_fe0_clk, "ahb1-fe0", "ahb1",
+ 0x064, BIT(14), 0);
+static SUNXI_CCU_GATE(ahb1_fe1_clk, "ahb1-fe1", "ahb1",
+ 0x064, BIT(15), 0);
+static SUNXI_CCU_GATE(ahb1_mp_clk, "ahb1-mp", "ahb1",
+ 0x064, BIT(18), 0);
+static SUNXI_CCU_GATE(ahb1_gpu_clk, "ahb1-gpu", "ahb1",
+ 0x064, BIT(20), 0);
+static SUNXI_CCU_GATE(ahb1_deu0_clk, "ahb1-deu0", "ahb1",
+ 0x064, BIT(23), 0);
+static SUNXI_CCU_GATE(ahb1_deu1_clk, "ahb1-deu1", "ahb1",
+ 0x064, BIT(24), 0);
+static SUNXI_CCU_GATE(ahb1_drc0_clk, "ahb1-drc0", "ahb1",
+ 0x064, BIT(25), 0);
+static SUNXI_CCU_GATE(ahb1_drc1_clk, "ahb1-drc1", "ahb1",
+ 0x064, BIT(26), 0);
+
+static SUNXI_CCU_GATE(apb1_codec_clk, "apb1-codec", "apb1",
+ 0x068, BIT(0), 0);
+static SUNXI_CCU_GATE(apb1_spdif_clk, "apb1-spdif", "apb1",
+ 0x068, BIT(1), 0);
+static SUNXI_CCU_GATE(apb1_digital_mic_clk, "apb1-digital-mic", "apb1",
+ 0x068, BIT(4), 0);
+static SUNXI_CCU_GATE(apb1_pio_clk, "apb1-pio", "apb1",
+ 0x068, BIT(5), 0);
+static SUNXI_CCU_GATE(apb1_daudio0_clk, "apb1-daudio0", "apb1",
+ 0x068, BIT(12), 0);
+static SUNXI_CCU_GATE(apb1_daudio1_clk, "apb1-daudio1", "apb1",
+ 0x068, BIT(13), 0);
+
+static SUNXI_CCU_GATE(apb2_i2c0_clk, "apb2-i2c0", "apb2",
+ 0x06c, BIT(0), 0);
+static SUNXI_CCU_GATE(apb2_i2c1_clk, "apb2-i2c1", "apb2",
+ 0x06c, BIT(1), 0);
+static SUNXI_CCU_GATE(apb2_i2c2_clk, "apb2-i2c2", "apb2",
+ 0x06c, BIT(2), 0);
+static SUNXI_CCU_GATE(apb2_i2c3_clk, "apb2-i2c3", "apb2",
+ 0x06c, BIT(3), 0);
+static SUNXI_CCU_GATE(apb2_uart0_clk, "apb2-uart0", "apb2",
+ 0x06c, BIT(16), 0);
+static SUNXI_CCU_GATE(apb2_uart1_clk, "apb2-uart1", "apb2",
+ 0x06c, BIT(17), 0);
+static SUNXI_CCU_GATE(apb2_uart2_clk, "apb2-uart2", "apb2",
+ 0x06c, BIT(18), 0);
+static SUNXI_CCU_GATE(apb2_uart3_clk, "apb2-uart3", "apb2",
+ 0x06c, BIT(19), 0);
+static SUNXI_CCU_GATE(apb2_uart4_clk, "apb2-uart4", "apb2",
+ 0x06c, BIT(20), 0);
+static SUNXI_CCU_GATE(apb2_uart5_clk, "apb2-uart5", "apb2",
+ 0x06c, BIT(21), 0);
+
+static const char * const mod0_default_parents[] = { "osc24M", "pll-periph" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand0_clk, "nand0", mod0_default_parents,
+ 0x080,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand1_clk, "nand1", mod0_default_parents,
+ 0x084,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents,
+ 0x088,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0",
+ 0x088, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0",
+ 0x088, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents,
+ 0x08c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1",
+ 0x08c, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1",
+ 0x08c, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents,
+ 0x090,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2",
+ 0x090, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2",
+ 0x090, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc3_clk, "mmc3", mod0_default_parents,
+ 0x094,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc3_sample_clk, "mmc3_sample", "mmc3",
+ 0x094, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc3_output_clk, "mmc3_output", "mmc3",
+ 0x094, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", mod0_default_parents, 0x098,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_default_parents, 0x09c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, 0x0a8,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi3_clk, "spi3", mod0_default_parents, 0x0ac,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const daudio_parents[] = { "pll-audio-8x", "pll-audio-4x",
+ "pll-audio-2x", "pll-audio" };
+static SUNXI_CCU_MUX_WITH_GATE(daudio0_clk, "daudio0", daudio_parents,
+ 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_MUX_WITH_GATE(daudio1_clk, "daudio1", daudio_parents,
+ 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_GATE(spdif_clk, "spdif", "pll-audio",
+ 0x0c0, 0, 4, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
+ 0x0cc, BIT(8), 0);
+static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M",
+ 0x0cc, BIT(9), 0);
+static SUNXI_CCU_GATE(usb_phy2_clk, "usb-phy2", "osc24M",
+ 0x0cc, BIT(10), 0);
+static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc24M",
+ 0x0cc, BIT(16), 0);
+static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "osc24M",
+ 0x0cc, BIT(17), 0);
+static SUNXI_CCU_GATE(usb_ohci2_clk, "usb-ohci2", "osc24M",
+ 0x0cc, BIT(18), 0);
+
+/* TODO emac clk not supported yet */
+
+static const char * const dram_parents[] = { "pll-ddr", "pll-periph" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(mdfs_clk, "mdfs", dram_parents, 0x0f0,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ CLK_IS_CRITICAL);
+
+static SUNXI_CCU_M_WITH_MUX(sdram0_clk, "sdram0", dram_parents,
+ 0x0f4, 0, 4, 4, 1, CLK_IS_CRITICAL);
+static SUNXI_CCU_M_WITH_MUX(sdram1_clk, "sdram1", dram_parents,
+ 0x0f4, 8, 4, 12, 1, CLK_IS_CRITICAL);
+
+static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "mdfs",
+ 0x100, BIT(0), 0);
+static SUNXI_CCU_GATE(dram_csi_isp_clk, "dram-csi-isp", "mdfs",
+ 0x100, BIT(1), 0);
+static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "mdfs",
+ 0x100, BIT(3), 0);
+static SUNXI_CCU_GATE(dram_drc0_clk, "dram-drc0", "mdfs",
+ 0x100, BIT(16), 0);
+static SUNXI_CCU_GATE(dram_drc1_clk, "dram-drc1", "mdfs",
+ 0x100, BIT(17), 0);
+static SUNXI_CCU_GATE(dram_deu0_clk, "dram-deu0", "mdfs",
+ 0x100, BIT(18), 0);
+static SUNXI_CCU_GATE(dram_deu1_clk, "dram-deu1", "mdfs",
+ 0x100, BIT(19), 0);
+static SUNXI_CCU_GATE(dram_fe0_clk, "dram-fe0", "mdfs",
+ 0x100, BIT(24), 0);
+static SUNXI_CCU_GATE(dram_fe1_clk, "dram-fe1", "mdfs",
+ 0x100, BIT(25), 0);
+static SUNXI_CCU_GATE(dram_be0_clk, "dram-be0", "mdfs",
+ 0x100, BIT(26), 0);
+static SUNXI_CCU_GATE(dram_be1_clk, "dram-be1", "mdfs",
+ 0x100, BIT(27), 0);
+static SUNXI_CCU_GATE(dram_mp_clk, "dram-mp", "mdfs",
+ 0x100, BIT(28), 0);
+
+static const char * const de_parents[] = { "pll-video0", "pll-video1",
+ "pll-periph-2x", "pll-gpu",
+ "pll9", "pll10" };
+static SUNXI_CCU_M_WITH_MUX_GATE(be0_clk, "be0", de_parents,
+ 0x104, 0, 4, 24, 3, BIT(31), 0);
+static SUNXI_CCU_M_WITH_MUX_GATE(be1_clk, "be1", de_parents,
+ 0x108, 0, 4, 24, 3, BIT(31), 0);
+static SUNXI_CCU_M_WITH_MUX_GATE(fe0_clk, "fe0", de_parents,
+ 0x10c, 0, 4, 24, 3, BIT(31), 0);
+static SUNXI_CCU_M_WITH_MUX_GATE(fe1_clk, "fe1", de_parents,
+ 0x110, 0, 4, 24, 3, BIT(31), 0);
+
+static const char * const mp_parents[] = { "pll-video0", "pll-video1",
+ "pll9", "pll10" };
+static SUNXI_CCU_M_WITH_MUX_GATE(mp_clk, "mp", mp_parents,
+ 0x114, 0, 4, 24, 3, BIT(31), 0);
+
+static const char * const lcd_ch0_parents[] = { "pll-video0", "pll-video1",
+ "pll-video0-2x",
+ "pll-video1-2x", "pll-mipi" };
+static SUNXI_CCU_MUX_WITH_GATE(lcd0_ch0_clk, "lcd0-ch0", lcd_ch0_parents,
+ 0x118, 24, 2, BIT(31), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_MUX_WITH_GATE(lcd1_ch0_clk, "lcd1-ch0", lcd_ch0_parents,
+ 0x11c, 24, 2, BIT(31), CLK_SET_RATE_PARENT);
+
+static const char * const lcd_ch1_parents[] = { "pll-video0", "pll-video1",
+ "pll-video0-2x",
+ "pll-video1-2x" };
+static SUNXI_CCU_M_WITH_MUX_GATE(lcd0_ch1_clk, "lcd0-ch1", lcd_ch1_parents,
+ 0x12c, 0, 4, 24, 3, BIT(31),
+ CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M_WITH_MUX_GATE(lcd1_ch1_clk, "lcd1-ch1", lcd_ch1_parents,
+ 0x12c, 0, 4, 24, 3, BIT(31),
+ CLK_SET_RATE_PARENT);
+
+static const char * const csi_sclk_parents[] = { "pll-video0", "pll-video1",
+ "pll9", "pll10", "pll-mipi",
+ "pll-ve" };
+static SUNXI_CCU_M_WITH_MUX_GATE(csi0_sclk_clk, "csi0-sclk", csi_sclk_parents,
+ 0x134, 16, 4, 24, 3, BIT(31), 0);
+
+static const char * const csi_mclk_parents[] = { "pll-video0", "pll-video1",
+ "osc24M" };
+static const u8 csi_mclk_table[] = { 0, 1, 5 };
+static struct ccu_div csi0_mclk_clk = {
+ .enable = BIT(15),
+ .div = _SUNXI_CCU_DIV(0, 4),
+ .mux = _SUNXI_CCU_MUX_TABLE(8, 3, csi_mclk_table),
+ .common = {
+ .reg = 0x134,
+ .hw.init = CLK_HW_INIT_PARENTS("csi0-mclk",
+ csi_mclk_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div csi1_mclk_clk = {
+ .enable = BIT(15),
+ .div = _SUNXI_CCU_DIV(0, 4),
+ .mux = _SUNXI_CCU_MUX_TABLE(8, 3, csi_mclk_table),
+ .common = {
+ .reg = 0x138,
+ .hw.init = CLK_HW_INIT_PARENTS("csi1-mclk",
+ csi_mclk_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve",
+ 0x13c, 16, 3, BIT(31), 0);
+
+static SUNXI_CCU_GATE(codec_clk, "codec", "pll-audio",
+ 0x140, BIT(31), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
+ 0x144, BIT(31), 0);
+static SUNXI_CCU_GATE(digital_mic_clk, "digital-mic", "pll-audio",
+ 0x148, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", lcd_ch1_parents,
+ 0x150, 0, 4, 24, 2, BIT(31),
+ CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(hdmi_ddc_clk, "hdmi-ddc", "osc24M", 0x150, BIT(31), 0);
+
+static SUNXI_CCU_GATE(ps_clk, "ps", "lcd1-ch1", 0x140, BIT(31), 0);
+
+static const char * const mbus_parents[] = { "osc24M", "pll-periph",
+ "pll-ddr" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(mbus0_clk, "mbus0", mbus_parents, 0x15c,
+ 0, 3, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ CLK_IS_CRITICAL);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mbus1_clk, "mbus1", mbus_parents, 0x160,
+ 0, 3, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ CLK_IS_CRITICAL);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(mipi_dsi_clk, "mipi-dsi", lcd_ch1_parents,
+ 0x168, 16, 3, 24, 2, BIT(31),
+ CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M_WITH_MUX_GATE(mipi_dsi_dphy_clk, "mipi-dsi-dphy",
+ lcd_ch1_parents, 0x168, 0, 3, 8, 2,
+ BIT(15), CLK_SET_RATE_PARENT);
+static SUNXI_CCU_M_WITH_MUX_GATE(mipi_csi_dphy_clk, "mipi-csi-dphy",
+ lcd_ch1_parents, 0x16c, 0, 3, 8, 2,
+ BIT(15), 0);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(iep_drc0_clk, "iep-drc0", de_parents,
+ 0x180, 0, 3, 24, 2, BIT(31), 0);
+static SUNXI_CCU_M_WITH_MUX_GATE(iep_drc1_clk, "iep-drc1", de_parents,
+ 0x184, 0, 3, 24, 2, BIT(31), 0);
+static SUNXI_CCU_M_WITH_MUX_GATE(iep_deu0_clk, "iep-deu0", de_parents,
+ 0x188, 0, 3, 24, 2, BIT(31), 0);
+static SUNXI_CCU_M_WITH_MUX_GATE(iep_deu1_clk, "iep-deu1", de_parents,
+ 0x18c, 0, 3, 24, 2, BIT(31), 0);
+
+static const char * const gpu_parents[] = { "pll-gpu", "pll-periph-2x",
+ "pll-video0", "pll-video1",
+ "pll9", "pll10" };
+static const struct ccu_mux_fixed_prediv gpu_predivs[] = {
+ { .index = 1, .div = 3, },
+};
+
+static struct ccu_div gpu_core_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV(0, 3),
+ .mux = {
+ .shift = 24,
+ .width = 3,
+ .fixed_predivs = gpu_predivs,
+ .n_predivs = ARRAY_SIZE(gpu_predivs),
+ },
+ .common = {
+ .reg = 0x1a0,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("gpu-core",
+ gpu_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div gpu_memory_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV(0, 3),
+ .mux = {
+ .shift = 24,
+ .width = 3,
+ .fixed_predivs = gpu_predivs,
+ .n_predivs = ARRAY_SIZE(gpu_predivs),
+ },
+ .common = {
+ .reg = 0x1a4,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("gpu-memory",
+ gpu_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_div gpu_hyd_clk = {
+ .enable = BIT(31),
+ .div = _SUNXI_CCU_DIV(0, 3),
+ .mux = {
+ .shift = 24,
+ .width = 3,
+ .fixed_predivs = gpu_predivs,
+ .n_predivs = ARRAY_SIZE(gpu_predivs),
+ },
+ .common = {
+ .reg = 0x1a8,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("gpu-hyd",
+ gpu_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", mod0_default_parents, 0x1b0,
+ 0, 3, /* M */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_M_WITH_MUX_GATE(trace_clk, "trace", mod0_default_parents,
+ 0x1b0,
+ 0, 3, /* M */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const clk_out_parents[] = { "osc24M", "osc32k", "osc24M",
+ "axi", "ahb1" };
+static const u8 clk_out_table[] = { 0, 1, 2, 11, 13 };
+
+static const struct ccu_mux_fixed_prediv clk_out_predivs[] = {
+ { .index = 0, .div = 750, },
+ { .index = 3, .div = 4, },
+ { .index = 4, .div = 4, },
+};
+
+static struct ccu_mp out_a_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(8, 5),
+ .p = _SUNXI_CCU_DIV(20, 2),
+ .mux = {
+ .shift = 24,
+ .width = 4,
+ .table = clk_out_table,
+ .fixed_predivs = clk_out_predivs,
+ .n_predivs = ARRAY_SIZE(clk_out_predivs),
+ },
+ .common = {
+ .reg = 0x300,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("out-a",
+ clk_out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_mp out_b_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(8, 5),
+ .p = _SUNXI_CCU_DIV(20, 2),
+ .mux = {
+ .shift = 24,
+ .width = 4,
+ .table = clk_out_table,
+ .fixed_predivs = clk_out_predivs,
+ .n_predivs = ARRAY_SIZE(clk_out_predivs),
+ },
+ .common = {
+ .reg = 0x304,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("out-b",
+ clk_out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_mp out_c_clk = {
+ .enable = BIT(31),
+ .m = _SUNXI_CCU_DIV(8, 5),
+ .p = _SUNXI_CCU_DIV(20, 2),
+ .mux = {
+ .shift = 24,
+ .width = 4,
+ .table = clk_out_table,
+ .fixed_predivs = clk_out_predivs,
+ .n_predivs = ARRAY_SIZE(clk_out_predivs),
+ },
+ .common = {
+ .reg = 0x308,
+ .features = CCU_FEATURE_FIXED_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("out-c",
+ clk_out_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct ccu_common *sun6i_a31_ccu_clks[] = {
+ &pll_cpu_clk.common,
+ &pll_audio_base_clk.common,
+ &pll_video0_clk.common,
+ &pll_ve_clk.common,
+ &pll_ddr_clk.common,
+ &pll_periph_clk.common,
+ &pll_video1_clk.common,
+ &pll_gpu_clk.common,
+ &pll_mipi_clk.common,
+ &pll9_clk.common,
+ &pll10_clk.common,
+ &cpu_clk.common,
+ &axi_clk.common,
+ &ahb1_clk.common,
+ &apb1_clk.common,
+ &apb2_clk.common,
+ &ahb1_mipidsi_clk.common,
+ &ahb1_ss_clk.common,
+ &ahb1_dma_clk.common,
+ &ahb1_mmc0_clk.common,
+ &ahb1_mmc1_clk.common,
+ &ahb1_mmc2_clk.common,
+ &ahb1_mmc3_clk.common,
+ &ahb1_nand1_clk.common,
+ &ahb1_nand0_clk.common,
+ &ahb1_sdram_clk.common,
+ &ahb1_emac_clk.common,
+ &ahb1_ts_clk.common,
+ &ahb1_hstimer_clk.common,
+ &ahb1_spi0_clk.common,
+ &ahb1_spi1_clk.common,
+ &ahb1_spi2_clk.common,
+ &ahb1_spi3_clk.common,
+ &ahb1_otg_clk.common,
+ &ahb1_ehci0_clk.common,
+ &ahb1_ehci1_clk.common,
+ &ahb1_ohci0_clk.common,
+ &ahb1_ohci1_clk.common,
+ &ahb1_ohci2_clk.common,
+ &ahb1_ve_clk.common,
+ &ahb1_lcd0_clk.common,
+ &ahb1_lcd1_clk.common,
+ &ahb1_csi_clk.common,
+ &ahb1_hdmi_clk.common,
+ &ahb1_be0_clk.common,
+ &ahb1_be1_clk.common,
+ &ahb1_fe0_clk.common,
+ &ahb1_fe1_clk.common,
+ &ahb1_mp_clk.common,
+ &ahb1_gpu_clk.common,
+ &ahb1_deu0_clk.common,
+ &ahb1_deu1_clk.common,
+ &ahb1_drc0_clk.common,
+ &ahb1_drc1_clk.common,
+ &apb1_codec_clk.common,
+ &apb1_spdif_clk.common,
+ &apb1_digital_mic_clk.common,
+ &apb1_pio_clk.common,
+ &apb1_daudio0_clk.common,
+ &apb1_daudio1_clk.common,
+ &apb2_i2c0_clk.common,
+ &apb2_i2c1_clk.common,
+ &apb2_i2c2_clk.common,
+ &apb2_i2c3_clk.common,
+ &apb2_uart0_clk.common,
+ &apb2_uart1_clk.common,
+ &apb2_uart2_clk.common,
+ &apb2_uart3_clk.common,
+ &apb2_uart4_clk.common,
+ &apb2_uart5_clk.common,
+ &nand0_clk.common,
+ &nand1_clk.common,
+ &mmc0_clk.common,
+ &mmc0_sample_clk.common,
+ &mmc0_output_clk.common,
+ &mmc1_clk.common,
+ &mmc1_sample_clk.common,
+ &mmc1_output_clk.common,
+ &mmc2_clk.common,
+ &mmc2_sample_clk.common,
+ &mmc2_output_clk.common,
+ &mmc3_clk.common,
+ &mmc3_sample_clk.common,
+ &mmc3_output_clk.common,
+ &ts_clk.common,
+ &ss_clk.common,
+ &spi0_clk.common,
+ &spi1_clk.common,
+ &spi2_clk.common,
+ &spi3_clk.common,
+ &daudio0_clk.common,
+ &daudio1_clk.common,
+ &spdif_clk.common,
+ &usb_phy0_clk.common,
+ &usb_phy1_clk.common,
+ &usb_phy2_clk.common,
+ &usb_ohci0_clk.common,
+ &usb_ohci1_clk.common,
+ &usb_ohci2_clk.common,
+ &mdfs_clk.common,
+ &sdram0_clk.common,
+ &sdram1_clk.common,
+ &dram_ve_clk.common,
+ &dram_csi_isp_clk.common,
+ &dram_ts_clk.common,
+ &dram_drc0_clk.common,
+ &dram_drc1_clk.common,
+ &dram_deu0_clk.common,
+ &dram_deu1_clk.common,
+ &dram_fe0_clk.common,
+ &dram_fe1_clk.common,
+ &dram_be0_clk.common,
+ &dram_be1_clk.common,
+ &dram_mp_clk.common,
+ &be0_clk.common,
+ &be1_clk.common,
+ &fe0_clk.common,
+ &fe1_clk.common,
+ &mp_clk.common,
+ &lcd0_ch0_clk.common,
+ &lcd1_ch0_clk.common,
+ &lcd0_ch1_clk.common,
+ &lcd1_ch1_clk.common,
+ &csi0_sclk_clk.common,
+ &csi0_mclk_clk.common,
+ &csi1_mclk_clk.common,
+ &ve_clk.common,
+ &codec_clk.common,
+ &avs_clk.common,
+ &digital_mic_clk.common,
+ &hdmi_clk.common,
+ &hdmi_ddc_clk.common,
+ &ps_clk.common,
+ &mbus0_clk.common,
+ &mbus1_clk.common,
+ &mipi_dsi_clk.common,
+ &mipi_dsi_dphy_clk.common,
+ &mipi_csi_dphy_clk.common,
+ &iep_drc0_clk.common,
+ &iep_drc1_clk.common,
+ &iep_deu0_clk.common,
+ &iep_deu1_clk.common,
+ &gpu_core_clk.common,
+ &gpu_memory_clk.common,
+ &gpu_hyd_clk.common,
+ &ats_clk.common,
+ &trace_clk.common,
+ &out_a_clk.common,
+ &out_b_clk.common,
+ &out_c_clk.common,
+};
+
+/* We hardcode the divider to 4 for now */
+static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio",
+ "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x",
+ "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x",
+ "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x",
+ "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_periph_2x_clk, "pll-periph-2x",
+ "pll-periph", 1, 2, 0);
+static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x",
+ "pll-video0", 1, 2, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_video1_2x_clk, "pll-video1-2x",
+ "pll-video1", 1, 2, CLK_SET_RATE_PARENT);
+
+static struct clk_hw_onecell_data sun6i_a31_hw_clks = {
+ .hws = {
+ [CLK_PLL_CPU] = &pll_cpu_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
+ [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw,
+ [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR] = &pll_ddr_clk.common.hw,
+ [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw,
+ [CLK_PLL_PERIPH_2X] = &pll_periph_2x_clk.hw,
+ [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw,
+ [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw,
+ [CLK_PLL_GPU] = &pll_gpu_clk.common.hw,
+ [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw,
+ [CLK_PLL9] = &pll9_clk.common.hw,
+ [CLK_PLL10] = &pll10_clk.common.hw,
+ [CLK_CPU] = &cpu_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_AHB1] = &ahb1_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_APB2] = &apb2_clk.common.hw,
+ [CLK_AHB1_MIPIDSI] = &ahb1_mipidsi_clk.common.hw,
+ [CLK_AHB1_SS] = &ahb1_ss_clk.common.hw,
+ [CLK_AHB1_DMA] = &ahb1_dma_clk.common.hw,
+ [CLK_AHB1_MMC0] = &ahb1_mmc0_clk.common.hw,
+ [CLK_AHB1_MMC1] = &ahb1_mmc1_clk.common.hw,
+ [CLK_AHB1_MMC2] = &ahb1_mmc2_clk.common.hw,
+ [CLK_AHB1_MMC3] = &ahb1_mmc3_clk.common.hw,
+ [CLK_AHB1_NAND1] = &ahb1_nand1_clk.common.hw,
+ [CLK_AHB1_NAND0] = &ahb1_nand0_clk.common.hw,
+ [CLK_AHB1_SDRAM] = &ahb1_sdram_clk.common.hw,
+ [CLK_AHB1_EMAC] = &ahb1_emac_clk.common.hw,
+ [CLK_AHB1_TS] = &ahb1_ts_clk.common.hw,
+ [CLK_AHB1_HSTIMER] = &ahb1_hstimer_clk.common.hw,
+ [CLK_AHB1_SPI0] = &ahb1_spi0_clk.common.hw,
+ [CLK_AHB1_SPI1] = &ahb1_spi1_clk.common.hw,
+ [CLK_AHB1_SPI2] = &ahb1_spi2_clk.common.hw,
+ [CLK_AHB1_SPI3] = &ahb1_spi3_clk.common.hw,
+ [CLK_AHB1_OTG] = &ahb1_otg_clk.common.hw,
+ [CLK_AHB1_EHCI0] = &ahb1_ehci0_clk.common.hw,
+ [CLK_AHB1_EHCI1] = &ahb1_ehci1_clk.common.hw,
+ [CLK_AHB1_OHCI0] = &ahb1_ohci0_clk.common.hw,
+ [CLK_AHB1_OHCI1] = &ahb1_ohci1_clk.common.hw,
+ [CLK_AHB1_OHCI2] = &ahb1_ohci2_clk.common.hw,
+ [CLK_AHB1_VE] = &ahb1_ve_clk.common.hw,
+ [CLK_AHB1_LCD0] = &ahb1_lcd0_clk.common.hw,
+ [CLK_AHB1_LCD1] = &ahb1_lcd1_clk.common.hw,
+ [CLK_AHB1_CSI] = &ahb1_csi_clk.common.hw,
+ [CLK_AHB1_HDMI] = &ahb1_hdmi_clk.common.hw,
+ [CLK_AHB1_BE0] = &ahb1_be0_clk.common.hw,
+ [CLK_AHB1_BE1] = &ahb1_be1_clk.common.hw,
+ [CLK_AHB1_FE0] = &ahb1_fe0_clk.common.hw,
+ [CLK_AHB1_FE1] = &ahb1_fe1_clk.common.hw,
+ [CLK_AHB1_MP] = &ahb1_mp_clk.common.hw,
+ [CLK_AHB1_GPU] = &ahb1_gpu_clk.common.hw,
+ [CLK_AHB1_DEU0] = &ahb1_deu0_clk.common.hw,
+ [CLK_AHB1_DEU1] = &ahb1_deu1_clk.common.hw,
+ [CLK_AHB1_DRC0] = &ahb1_drc0_clk.common.hw,
+ [CLK_AHB1_DRC1] = &ahb1_drc1_clk.common.hw,
+ [CLK_APB1_CODEC] = &apb1_codec_clk.common.hw,
+ [CLK_APB1_SPDIF] = &apb1_spdif_clk.common.hw,
+ [CLK_APB1_DIGITAL_MIC] = &apb1_digital_mic_clk.common.hw,
+ [CLK_APB1_PIO] = &apb1_pio_clk.common.hw,
+ [CLK_APB1_DAUDIO0] = &apb1_daudio0_clk.common.hw,
+ [CLK_APB1_DAUDIO1] = &apb1_daudio1_clk.common.hw,
+ [CLK_APB2_I2C0] = &apb2_i2c0_clk.common.hw,
+ [CLK_APB2_I2C1] = &apb2_i2c1_clk.common.hw,
+ [CLK_APB2_I2C2] = &apb2_i2c2_clk.common.hw,
+ [CLK_APB2_I2C3] = &apb2_i2c3_clk.common.hw,
+ [CLK_APB2_UART0] = &apb2_uart0_clk.common.hw,
+ [CLK_APB2_UART1] = &apb2_uart1_clk.common.hw,
+ [CLK_APB2_UART2] = &apb2_uart2_clk.common.hw,
+ [CLK_APB2_UART3] = &apb2_uart3_clk.common.hw,
+ [CLK_APB2_UART4] = &apb2_uart4_clk.common.hw,
+ [CLK_APB2_UART5] = &apb2_uart5_clk.common.hw,
+ [CLK_NAND0] = &nand0_clk.common.hw,
+ [CLK_NAND1] = &nand1_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw,
+ [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw,
+ [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw,
+ [CLK_MMC2] = &mmc2_clk.common.hw,
+ [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw,
+ [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw,
+ [CLK_MMC3] = &mmc3_clk.common.hw,
+ [CLK_MMC3_SAMPLE] = &mmc3_sample_clk.common.hw,
+ [CLK_MMC3_OUTPUT] = &mmc3_output_clk.common.hw,
+ [CLK_TS] = &ts_clk.common.hw,
+ [CLK_SS] = &ss_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_SPI2] = &spi2_clk.common.hw,
+ [CLK_SPI3] = &spi3_clk.common.hw,
+ [CLK_DAUDIO0] = &daudio0_clk.common.hw,
+ [CLK_DAUDIO1] = &daudio1_clk.common.hw,
+ [CLK_SPDIF] = &spdif_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_PHY1] = &usb_phy1_clk.common.hw,
+ [CLK_USB_PHY2] = &usb_phy2_clk.common.hw,
+ [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw,
+ [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw,
+ [CLK_USB_OHCI2] = &usb_ohci2_clk.common.hw,
+ [CLK_MDFS] = &mdfs_clk.common.hw,
+ [CLK_SDRAM0] = &sdram0_clk.common.hw,
+ [CLK_SDRAM1] = &sdram1_clk.common.hw,
+ [CLK_DRAM_VE] = &dram_ve_clk.common.hw,
+ [CLK_DRAM_CSI_ISP] = &dram_csi_isp_clk.common.hw,
+ [CLK_DRAM_TS] = &dram_ts_clk.common.hw,
+ [CLK_DRAM_DRC0] = &dram_drc0_clk.common.hw,
+ [CLK_DRAM_DRC1] = &dram_drc1_clk.common.hw,
+ [CLK_DRAM_DEU0] = &dram_deu0_clk.common.hw,
+ [CLK_DRAM_DEU1] = &dram_deu1_clk.common.hw,
+ [CLK_DRAM_FE0] = &dram_fe0_clk.common.hw,
+ [CLK_DRAM_FE1] = &dram_fe1_clk.common.hw,
+ [CLK_DRAM_BE0] = &dram_be0_clk.common.hw,
+ [CLK_DRAM_BE1] = &dram_be1_clk.common.hw,
+ [CLK_DRAM_MP] = &dram_mp_clk.common.hw,
+ [CLK_BE0] = &be0_clk.common.hw,
+ [CLK_BE1] = &be1_clk.common.hw,
+ [CLK_FE0] = &fe0_clk.common.hw,
+ [CLK_FE1] = &fe1_clk.common.hw,
+ [CLK_MP] = &mp_clk.common.hw,
+ [CLK_LCD0_CH0] = &lcd0_ch0_clk.common.hw,
+ [CLK_LCD1_CH0] = &lcd1_ch0_clk.common.hw,
+ [CLK_LCD0_CH1] = &lcd0_ch1_clk.common.hw,
+ [CLK_LCD1_CH1] = &lcd1_ch1_clk.common.hw,
+ [CLK_CSI0_SCLK] = &csi0_sclk_clk.common.hw,
+ [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw,
+ [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_CODEC] = &codec_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_DIGITAL_MIC] = &digital_mic_clk.common.hw,
+ [CLK_HDMI] = &hdmi_clk.common.hw,
+ [CLK_HDMI_DDC] = &hdmi_ddc_clk.common.hw,
+ [CLK_PS] = &ps_clk.common.hw,
+ [CLK_MBUS0] = &mbus0_clk.common.hw,
+ [CLK_MBUS1] = &mbus1_clk.common.hw,
+ [CLK_MIPI_DSI] = &mipi_dsi_clk.common.hw,
+ [CLK_MIPI_DSI_DPHY] = &mipi_dsi_dphy_clk.common.hw,
+ [CLK_MIPI_CSI_DPHY] = &mipi_csi_dphy_clk.common.hw,
+ [CLK_IEP_DRC0] = &iep_drc0_clk.common.hw,
+ [CLK_IEP_DRC1] = &iep_drc1_clk.common.hw,
+ [CLK_IEP_DEU0] = &iep_deu0_clk.common.hw,
+ [CLK_IEP_DEU1] = &iep_deu1_clk.common.hw,
+ [CLK_GPU_CORE] = &gpu_core_clk.common.hw,
+ [CLK_GPU_MEMORY] = &gpu_memory_clk.common.hw,
+ [CLK_GPU_HYD] = &gpu_hyd_clk.common.hw,
+ [CLK_ATS] = &ats_clk.common.hw,
+ [CLK_TRACE] = &trace_clk.common.hw,
+ [CLK_OUT_A] = &out_a_clk.common.hw,
+ [CLK_OUT_B] = &out_b_clk.common.hw,
+ [CLK_OUT_C] = &out_c_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun6i_a31_ccu_resets[] = {
+ [RST_USB_PHY0] = { 0x0cc, BIT(0) },
+ [RST_USB_PHY1] = { 0x0cc, BIT(1) },
+ [RST_USB_PHY2] = { 0x0cc, BIT(2) },
+
+ [RST_AHB1_MIPI_DSI] = { 0x2c0, BIT(1) },
+ [RST_AHB1_SS] = { 0x2c0, BIT(5) },
+ [RST_AHB1_DMA] = { 0x2c0, BIT(6) },
+ [RST_AHB1_MMC0] = { 0x2c0, BIT(8) },
+ [RST_AHB1_MMC1] = { 0x2c0, BIT(9) },
+ [RST_AHB1_MMC2] = { 0x2c0, BIT(10) },
+ [RST_AHB1_MMC3] = { 0x2c0, BIT(11) },
+ [RST_AHB1_NAND1] = { 0x2c0, BIT(12) },
+ [RST_AHB1_NAND0] = { 0x2c0, BIT(13) },
+ [RST_AHB1_SDRAM] = { 0x2c0, BIT(14) },
+ [RST_AHB1_EMAC] = { 0x2c0, BIT(17) },
+ [RST_AHB1_TS] = { 0x2c0, BIT(18) },
+ [RST_AHB1_HSTIMER] = { 0x2c0, BIT(19) },
+ [RST_AHB1_SPI0] = { 0x2c0, BIT(20) },
+ [RST_AHB1_SPI1] = { 0x2c0, BIT(21) },
+ [RST_AHB1_SPI2] = { 0x2c0, BIT(22) },
+ [RST_AHB1_SPI3] = { 0x2c0, BIT(23) },
+ [RST_AHB1_OTG] = { 0x2c0, BIT(24) },
+ [RST_AHB1_EHCI0] = { 0x2c0, BIT(26) },
+ [RST_AHB1_EHCI1] = { 0x2c0, BIT(27) },
+ [RST_AHB1_OHCI0] = { 0x2c0, BIT(29) },
+ [RST_AHB1_OHCI1] = { 0x2c0, BIT(30) },
+ [RST_AHB1_OHCI2] = { 0x2c0, BIT(31) },
+
+ [RST_AHB1_VE] = { 0x2c4, BIT(0) },
+ [RST_AHB1_LCD0] = { 0x2c4, BIT(4) },
+ [RST_AHB1_LCD1] = { 0x2c4, BIT(5) },
+ [RST_AHB1_CSI] = { 0x2c4, BIT(8) },
+ [RST_AHB1_HDMI] = { 0x2c4, BIT(11) },
+ [RST_AHB1_BE0] = { 0x2c4, BIT(12) },
+ [RST_AHB1_BE1] = { 0x2c4, BIT(13) },
+ [RST_AHB1_FE0] = { 0x2c4, BIT(14) },
+ [RST_AHB1_FE1] = { 0x2c4, BIT(15) },
+ [RST_AHB1_MP] = { 0x2c4, BIT(18) },
+ [RST_AHB1_GPU] = { 0x2c4, BIT(20) },
+ [RST_AHB1_DEU0] = { 0x2c4, BIT(23) },
+ [RST_AHB1_DEU1] = { 0x2c4, BIT(24) },
+ [RST_AHB1_DRC0] = { 0x2c4, BIT(25) },
+ [RST_AHB1_DRC1] = { 0x2c4, BIT(26) },
+ [RST_AHB1_LVDS] = { 0x2c8, BIT(0) },
+
+ [RST_APB1_CODEC] = { 0x2d0, BIT(0) },
+ [RST_APB1_SPDIF] = { 0x2d0, BIT(1) },
+ [RST_APB1_DIGITAL_MIC] = { 0x2d0, BIT(4) },
+ [RST_APB1_DAUDIO0] = { 0x2d0, BIT(12) },
+ [RST_APB1_DAUDIO1] = { 0x2d0, BIT(13) },
+
+ [RST_APB2_I2C0] = { 0x2d8, BIT(0) },
+ [RST_APB2_I2C1] = { 0x2d8, BIT(1) },
+ [RST_APB2_I2C2] = { 0x2d8, BIT(2) },
+ [RST_APB2_I2C3] = { 0x2d8, BIT(3) },
+ [RST_APB2_UART0] = { 0x2d8, BIT(16) },
+ [RST_APB2_UART1] = { 0x2d8, BIT(17) },
+ [RST_APB2_UART2] = { 0x2d8, BIT(18) },
+ [RST_APB2_UART3] = { 0x2d8, BIT(19) },
+ [RST_APB2_UART4] = { 0x2d8, BIT(20) },
+ [RST_APB2_UART5] = { 0x2d8, BIT(21) },
+};
+
+static const struct sunxi_ccu_desc sun6i_a31_ccu_desc = {
+ .ccu_clks = sun6i_a31_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun6i_a31_ccu_clks),
+
+ .hw_clks = &sun6i_a31_hw_clks,
+
+ .resets = sun6i_a31_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun6i_a31_ccu_resets),
+};
+
+static struct ccu_mux_nb sun6i_a31_cpu_nb = {
+ .common = &cpu_clk.common,
+ .cm = &cpu_clk.mux,
+ .delay_us = 1, /* > 8 clock cycles at 24 MHz */
+ .bypass_index = 1, /* index of 24 MHz oscillator */
+};
+
+static void __init sun6i_a31_ccu_setup(struct device_node *node)
+{
+ void __iomem *reg;
+ u32 val;
+
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (IS_ERR(reg)) {
+ pr_err("%s: Could not map the clock registers\n",
+ of_node_full_name(node));
+ return;
+ }
+
+ /* Force the PLL-Audio-1x divider to 4 */
+ val = readl(reg + SUN6I_A31_PLL_AUDIO_REG);
+ val &= ~GENMASK(19, 16);
+ writel(val | (3 << 16), reg + SUN6I_A31_PLL_AUDIO_REG);
+
+ /* Force PLL-MIPI to MIPI mode */
+ val = readl(reg + SUN6I_A31_PLL_MIPI_REG);
+ val &= BIT(16);
+ writel(val, reg + SUN6I_A31_PLL_MIPI_REG);
+
+ sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc);
+
+ ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk,
+ &sun6i_a31_cpu_nb);
+}
+CLK_OF_DECLARE(sun6i_a31_ccu, "allwinner,sun6i-a31-ccu",
+ sun6i_a31_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.h b/drivers/clk/sunxi-ng/ccu-sun6i-a31.h
new file mode 100644
index 0000000..4e43401
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Chen-Yu Tsai
+ *
+ * Chen-Yu Tsai <wens@csie.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN6I_A31_H_
+#define _CCU_SUN6I_A31_H_
+
+#include <dt-bindings/clock/sun6i-a31-ccu.h>
+#include <dt-bindings/reset/sun6i-a31-ccu.h>
+
+#define CLK_PLL_CPU 0
+#define CLK_PLL_AUDIO_BASE 1
+#define CLK_PLL_AUDIO 2
+#define CLK_PLL_AUDIO_2X 3
+#define CLK_PLL_AUDIO_4X 4
+#define CLK_PLL_AUDIO_8X 5
+#define CLK_PLL_VIDEO0 6
+#define CLK_PLL_VIDEO0_2X 7
+#define CLK_PLL_VE 8
+#define CLK_PLL_DDR 9
+
+/* The PLL_PERIPH clock is exported */
+
+#define CLK_PLL_PERIPH_2X 11
+#define CLK_PLL_VIDEO1 12
+#define CLK_PLL_VIDEO1_2X 13
+#define CLK_PLL_GPU 14
+#define CLK_PLL_MIPI 15
+#define CLK_PLL9 16
+#define CLK_PLL10 17
+
+/* The CPUX clock is exported */
+
+#define CLK_AXI 19
+#define CLK_AHB1 20
+#define CLK_APB1 21
+#define CLK_APB2 22
+
+/* All the bus gates are exported */
+
+/* The first bunch of module clocks are exported */
+
+/* EMAC clock is not implemented */
+
+#define CLK_MDFS 107
+#define CLK_SDRAM0 108
+#define CLK_SDRAM1 109
+
+/* All the DRAM gates are exported */
+
+/* Some more module clocks are exported */
+
+#define CLK_MBUS0 141
+#define CLK_MBUS1 142
+
+/* Some more module clocks and external clock outputs are exported */
+
+#define CLK_NUMBER (CLK_OUT_C + 1)
+
+#endif /* _CCU_SUN6I_A31_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23-a33.h b/drivers/clk/sunxi-ng/ccu-sun8i-a23-a33.h
new file mode 100644
index 0000000..62c0f8d
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23-a33.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2016 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CCU_SUN8I_A23_A33_H_
+#define _CCU_SUN8I_A23_A33_H_
+
+#include <dt-bindings/clock/sun8i-a23-a33-ccu.h>
+#include <dt-bindings/reset/sun8i-a23-a33-ccu.h>
+
+#define CLK_PLL_CPUX 0
+#define CLK_PLL_AUDIO_BASE 1
+#define CLK_PLL_AUDIO 2
+#define CLK_PLL_AUDIO_2X 3
+#define CLK_PLL_AUDIO_4X 4
+#define CLK_PLL_AUDIO_8X 5
+#define CLK_PLL_VIDEO 6
+#define CLK_PLL_VIDEO_2X 7
+#define CLK_PLL_VE 8
+#define CLK_PLL_DDR0 9
+#define CLK_PLL_PERIPH 10
+#define CLK_PLL_PERIPH_2X 11
+#define CLK_PLL_GPU 12
+#define CLK_PLL_MIPI 13
+#define CLK_PLL_HSIC 14
+#define CLK_PLL_DE 15
+#define CLK_PLL_DDR1 16
+#define CLK_PLL_DDR 17
+
+/* The CPUX clock is exported */
+
+#define CLK_AXI 19
+#define CLK_AHB1 20
+#define CLK_APB1 21
+#define CLK_APB2 22
+
+/* All the bus gates are exported */
+
+/* The first part of the mod clocks is exported */
+
+#define CLK_DRAM 79
+
+/* Some more module clocks are exported */
+
+#define CLK_MBUS 95
+
+/* And the last module clocks are exported */
+
+#define CLK_NUMBER (CLK_ATS + 1)
+
+#endif /* _CCU_SUN8I_A23_A33_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
new file mode 100644
index 0000000..2646d98
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (c) 2016 Maxime Ripard. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_mult.h"
+#include "ccu_nk.h"
+#include "ccu_nkm.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+#include "ccu_phase.h"
+
+#include "ccu-sun8i-a23-a33.h"
+
+
+static struct ccu_nkmp pll_cpux_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+
+ .n = _SUNXI_CCU_MULT(8, 5),
+ .k = _SUNXI_CCU_MULT(4, 2),
+ .m = _SUNXI_CCU_DIV(0, 2),
+ .p = _SUNXI_CCU_DIV_MAX(16, 2, 4),
+
+ .common = {
+ .reg = 0x000,
+ .hw.init = CLK_HW_INIT("pll-cpux", "osc24M",
+ &ccu_nkmp_ops,
+ 0),
+ },
+};
+
+/*
+ * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from
+ * the base (2x, 4x and 8x), and one variable divider (the one true
+ * pll audio).
+ *
+ * We don't have any need for the variable divider for now, so we just
+ * hardcode it to match with the clock names
+ */
+#define SUN8I_A23_PLL_AUDIO_REG 0x008
+
+static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base",
+ "osc24M", 0x008,
+ 8, 7, /* N */
+ 0, 5, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video",
+ "osc24M", 0x010,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve",
+ "osc24M", 0x018,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr_clk, "pll-ddr",
+ "osc24M", 0x020,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ 0, 2, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph_clk, "pll-periph",
+ "osc24M", 0x028,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 2, /* post-div */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu",
+ "osc24M", 0x038,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+/*
+ * The MIPI PLL has 2 modes: "MIPI" and "HDMI".
+ *
+ * The MIPI mode is a standard NKM-style clock. The HDMI mode is an
+ * integer / fractional clock with switchable multipliers and dividers.
+ * This is not supported here. We hardcode the PLL to MIPI mode.
+ */
+#define SUN8I_A23_PLL_MIPI_REG 0x040
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi",
+ "pll-video", 0x040,
+ 8, 4, /* N */
+ 4, 2, /* K */
+ 0, 4, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_hsic_clk, "pll-hsic",
+ "osc24M", 0x044,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de",
+ "osc24M", 0x048,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static const char * const cpux_parents[] = { "osc32k", "osc24M",
+ "pll-cpux" , "pll-cpux" };
+static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents,
+ 0x050, 16, 2, CLK_IS_CRITICAL);
+
+static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0);
+
+static const char * const ahb1_parents[] = { "osc32k", "osc24M",
+ "axi" , "pll-periph" };
+static struct ccu_div ahb1_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO),
+
+ .mux = {
+ .shift = 12,
+ .width = 2,
+
+ .variable_prediv = {
+ .index = 3,
+ .shift = 6,
+ .width = 2,
+ },
+ },
+
+ .common = {
+ .reg = 0x054,
+ .features = CCU_FEATURE_VARIABLE_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb1",
+ ahb1_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct clk_div_table apb1_div_table[] = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 8 },
+ { /* Sentinel */ },
+};
+static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1",
+ 0x054, 8, 2, apb1_div_table, 0);
+
+static const char * const apb2_parents[] = { "osc32k", "osc24M",
+ "pll-periph" , "pll-periph" };
+static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb1",
+ 0x060, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1",
+ 0x060, BIT(6), 0);
+static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1",
+ 0x060, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1",
+ 0x060, BIT(9), 0);
+static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1",
+ 0x060, BIT(10), 0);
+static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb1",
+ 0x060, BIT(13), 0);
+static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1",
+ 0x060, BIT(14), 0);
+static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1",
+ 0x060, BIT(19), 0);
+static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1",
+ 0x060, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb1",
+ 0x060, BIT(21), 0);
+static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1",
+ 0x060, BIT(24), 0);
+static SUNXI_CCU_GATE(bus_ehci_clk, "bus-ehci", "ahb1",
+ 0x060, BIT(26), 0);
+static SUNXI_CCU_GATE(bus_ohci_clk, "bus-ohci", "ahb1",
+ 0x060, BIT(29), 0);
+
+static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1",
+ 0x064, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_lcd_clk, "bus-lcd", "ahb1",
+ 0x064, BIT(4), 0);
+static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb1",
+ 0x064, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_de_be_clk, "bus-de-be", "ahb1",
+ 0x064, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_de_fe_clk, "bus-de-fe", "ahb1",
+ 0x064, BIT(14), 0);
+static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "ahb1",
+ 0x064, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb1",
+ 0x064, BIT(21), 0);
+static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb1",
+ 0x064, BIT(22), 0);
+static SUNXI_CCU_GATE(bus_drc_clk, "bus-drc", "ahb1",
+ 0x064, BIT(25), 0);
+
+static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1",
+ 0x068, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1",
+ 0x068, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1",
+ 0x068, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1",
+ 0x068, BIT(13), 0);
+
+static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2",
+ 0x06c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2",
+ 0x06c, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2",
+ 0x06c, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2",
+ 0x06c, BIT(16), 0);
+static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2",
+ 0x06c, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2",
+ 0x06c, BIT(18), 0);
+static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2",
+ 0x06c, BIT(19), 0);
+static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb2",
+ 0x06c, BIT(20), 0);
+
+static const char * const mod0_default_parents[] = { "osc24M", "pll-periph" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0",
+ 0x088, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0",
+ 0x088, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1",
+ 0x08c, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1",
+ 0x08c, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2",
+ 0x090, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2",
+ 0x090, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x",
+ "pll-audio-2x", "pll-audio" };
+static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents,
+ 0x0b0, 16, 2, BIT(31), 0);
+
+static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents,
+ 0x0b4, 16, 2, BIT(31), 0);
+
+/* TODO: the parent for most of the USB clocks is not known */
+static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
+ 0x0cc, BIT(8), 0);
+static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M",
+ 0x0cc, BIT(9), 0);
+static SUNXI_CCU_GATE(usb_hsic_clk, "usb-hsic", "pll-hsic",
+ 0x0cc, BIT(10), 0);
+static SUNXI_CCU_GATE(usb_hsic_12M_clk, "usb-hsic-12M", "osc24M",
+ 0x0cc, BIT(11), 0);
+static SUNXI_CCU_GATE(usb_ohci_clk, "usb-ohci", "osc24M",
+ 0x0cc, BIT(16), 0);
+
+static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "pll-ddr",
+ 0x100, BIT(0), 0);
+static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "pll-ddr",
+ 0x100, BIT(1), 0);
+static SUNXI_CCU_GATE(dram_drc_clk, "dram-drc", "pll-ddr",
+ 0x100, BIT(16), 0);
+static SUNXI_CCU_GATE(dram_de_fe_clk, "dram-de-fe", "pll-ddr",
+ 0x100, BIT(24), 0);
+static SUNXI_CCU_GATE(dram_de_be_clk, "dram-de-be", "pll-ddr",
+ 0x100, BIT(26), 0);
+
+static const char * const de_parents[] = { "pll-video", "pll-periph-2x",
+ "pll-gpu", "pll-de" };
+static const u8 de_table[] = { 0, 2, 3, 5 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(de_be_clk, "de-be",
+ de_parents, de_table,
+ 0x104, 0, 4, 24, 3, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(de_fe_clk, "de-fe",
+ de_parents, de_table,
+ 0x10c, 0, 4, 24, 3, BIT(31), 0);
+
+static const char * const lcd_ch0_parents[] = { "pll-video", "pll-video-2x",
+ "pll-mipi" };
+static const u8 lcd_ch0_table[] = { 0, 2, 4 };
+static SUNXI_CCU_MUX_TABLE_WITH_GATE(lcd_ch0_clk, "lcd-ch0",
+ lcd_ch0_parents, lcd_ch0_table,
+ 0x118, 24, 3, BIT(31),
+ CLK_SET_RATE_PARENT);
+
+static const char * const lcd_ch1_parents[] = { "pll-video", "pll-video-2x" };
+static const u8 lcd_ch1_table[] = { 0, 2 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(lcd_ch1_clk, "lcd-ch1",
+ lcd_ch1_parents, lcd_ch1_table,
+ 0x12c, 0, 4, 24, 2, BIT(31), 0);
+
+static const char * const csi_sclk_parents[] = { "pll-video", "pll-de",
+ "pll-mipi", "pll-ve" };
+static const u8 csi_sclk_table[] = { 0, 3, 4, 5 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_sclk_clk, "csi-sclk",
+ csi_sclk_parents, csi_sclk_table,
+ 0x134, 16, 4, 24, 3, BIT(31), 0);
+
+static const char * const csi_mclk_parents[] = { "pll-video", "pll-de",
+ "osc24M" };
+static const u8 csi_mclk_table[] = { 0, 3, 5 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_mclk_clk, "csi-mclk",
+ csi_mclk_parents, csi_mclk_table,
+ 0x134, 0, 5, 8, 3, BIT(15), 0);
+
+static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve",
+ 0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio",
+ 0x140, BIT(31), 0);
+static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
+ 0x144, BIT(31), 0);
+
+static const char * const mbus_parents[] = { "osc24M", "pll-periph-2x",
+ "pll-ddr" };
+static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents,
+ 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL);
+
+static const char * const dsi_sclk_parents[] = { "pll-video", "pll-video-2x" };
+static const u8 dsi_sclk_table[] = { 0, 2 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_sclk_clk, "dsi-sclk",
+ dsi_sclk_parents, dsi_sclk_table,
+ 0x168, 16, 4, 24, 2, BIT(31), 0);
+
+static const char * const dsi_dphy_parents[] = { "pll-video", "pll-periph" };
+static const u8 dsi_dphy_table[] = { 0, 2 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy",
+ dsi_dphy_parents, dsi_dphy_table,
+ 0x168, 0, 4, 8, 2, BIT(15), 0);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(drc_clk, "drc",
+ de_parents, de_table,
+ 0x180, 0, 4, 24, 3, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu",
+ 0x1a0, 0, 3, BIT(31), 0);
+
+static const char * const ats_parents[] = { "osc24M", "pll-periph" };
+static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", ats_parents,
+ 0x1b0, 0, 3, 24, 2, BIT(31), 0);
+
+static struct ccu_common *sun8i_a23_ccu_clks[] = {
+ &pll_cpux_clk.common,
+ &pll_audio_base_clk.common,
+ &pll_video_clk.common,
+ &pll_ve_clk.common,
+ &pll_ddr_clk.common,
+ &pll_periph_clk.common,
+ &pll_gpu_clk.common,
+ &pll_mipi_clk.common,
+ &pll_hsic_clk.common,
+ &pll_de_clk.common,
+ &cpux_clk.common,
+ &axi_clk.common,
+ &ahb1_clk.common,
+ &apb1_clk.common,
+ &apb2_clk.common,
+ &bus_mipi_dsi_clk.common,
+ &bus_dma_clk.common,
+ &bus_mmc0_clk.common,
+ &bus_mmc1_clk.common,
+ &bus_mmc2_clk.common,
+ &bus_nand_clk.common,
+ &bus_dram_clk.common,
+ &bus_hstimer_clk.common,
+ &bus_spi0_clk.common,
+ &bus_spi1_clk.common,
+ &bus_otg_clk.common,
+ &bus_ehci_clk.common,
+ &bus_ohci_clk.common,
+ &bus_ve_clk.common,
+ &bus_lcd_clk.common,
+ &bus_csi_clk.common,
+ &bus_de_fe_clk.common,
+ &bus_de_be_clk.common,
+ &bus_gpu_clk.common,
+ &bus_msgbox_clk.common,
+ &bus_spinlock_clk.common,
+ &bus_drc_clk.common,
+ &bus_codec_clk.common,
+ &bus_pio_clk.common,
+ &bus_i2s0_clk.common,
+ &bus_i2s1_clk.common,
+ &bus_i2c0_clk.common,
+ &bus_i2c1_clk.common,
+ &bus_i2c2_clk.common,
+ &bus_uart0_clk.common,
+ &bus_uart1_clk.common,
+ &bus_uart2_clk.common,
+ &bus_uart3_clk.common,
+ &bus_uart4_clk.common,
+ &nand_clk.common,
+ &mmc0_clk.common,
+ &mmc0_sample_clk.common,
+ &mmc0_output_clk.common,
+ &mmc1_clk.common,
+ &mmc1_sample_clk.common,
+ &mmc1_output_clk.common,
+ &mmc2_clk.common,
+ &mmc2_sample_clk.common,
+ &mmc2_output_clk.common,
+ &spi0_clk.common,
+ &spi1_clk.common,
+ &i2s0_clk.common,
+ &i2s1_clk.common,
+ &usb_phy0_clk.common,
+ &usb_phy1_clk.common,
+ &usb_hsic_clk.common,
+ &usb_hsic_12M_clk.common,
+ &usb_ohci_clk.common,
+ &dram_ve_clk.common,
+ &dram_csi_clk.common,
+ &dram_drc_clk.common,
+ &dram_de_fe_clk.common,
+ &dram_de_be_clk.common,
+ &de_be_clk.common,
+ &de_fe_clk.common,
+ &lcd_ch0_clk.common,
+ &lcd_ch1_clk.common,
+ &csi_sclk_clk.common,
+ &csi_mclk_clk.common,
+ &ve_clk.common,
+ &ac_dig_clk.common,
+ &avs_clk.common,
+ &mbus_clk.common,
+ &dsi_sclk_clk.common,
+ &dsi_dphy_clk.common,
+ &drc_clk.common,
+ &gpu_clk.common,
+ &ats_clk.common,
+};
+
+/* We hardcode the divider to 4 for now */
+static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio",
+ "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x",
+ "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x",
+ "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x",
+ "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_periph_2x_clk, "pll-periph-2x",
+ "pll-periph", 1, 2, 0);
+static CLK_FIXED_FACTOR(pll_video_2x_clk, "pll-video-2x",
+ "pll-video", 1, 2, 0);
+
+static struct clk_hw_onecell_data sun8i_a23_hw_clks = {
+ .hws = {
+ [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
+ [CLK_PLL_VIDEO] = &pll_video_clk.common.hw,
+ [CLK_PLL_VIDEO_2X] = &pll_video_2x_clk.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR0] = &pll_ddr_clk.common.hw,
+ [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw,
+ [CLK_PLL_PERIPH_2X] = &pll_periph_2x_clk.hw,
+ [CLK_PLL_GPU] = &pll_gpu_clk.common.hw,
+ [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw,
+ [CLK_PLL_HSIC] = &pll_hsic_clk.common.hw,
+ [CLK_PLL_DE] = &pll_de_clk.common.hw,
+ [CLK_CPUX] = &cpux_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_AHB1] = &ahb1_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_APB2] = &apb2_clk.common.hw,
+ [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw,
+ [CLK_BUS_DMA] = &bus_dma_clk.common.hw,
+ [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw,
+ [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw,
+ [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw,
+ [CLK_BUS_NAND] = &bus_nand_clk.common.hw,
+ [CLK_BUS_DRAM] = &bus_dram_clk.common.hw,
+ [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw,
+ [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw,
+ [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw,
+ [CLK_BUS_OTG] = &bus_otg_clk.common.hw,
+ [CLK_BUS_EHCI] = &bus_ehci_clk.common.hw,
+ [CLK_BUS_OHCI] = &bus_ohci_clk.common.hw,
+ [CLK_BUS_VE] = &bus_ve_clk.common.hw,
+ [CLK_BUS_LCD] = &bus_lcd_clk.common.hw,
+ [CLK_BUS_CSI] = &bus_csi_clk.common.hw,
+ [CLK_BUS_DE_BE] = &bus_de_be_clk.common.hw,
+ [CLK_BUS_DE_FE] = &bus_de_fe_clk.common.hw,
+ [CLK_BUS_GPU] = &bus_gpu_clk.common.hw,
+ [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw,
+ [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw,
+ [CLK_BUS_DRC] = &bus_drc_clk.common.hw,
+ [CLK_BUS_CODEC] = &bus_codec_clk.common.hw,
+ [CLK_BUS_PIO] = &bus_pio_clk.common.hw,
+ [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw,
+ [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw,
+ [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw,
+ [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw,
+ [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw,
+ [CLK_BUS_UART0] = &bus_uart0_clk.common.hw,
+ [CLK_BUS_UART1] = &bus_uart1_clk.common.hw,
+ [CLK_BUS_UART2] = &bus_uart2_clk.common.hw,
+ [CLK_BUS_UART3] = &bus_uart3_clk.common.hw,
+ [CLK_BUS_UART4] = &bus_uart4_clk.common.hw,
+ [CLK_NAND] = &nand_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw,
+ [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw,
+ [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw,
+ [CLK_MMC2] = &mmc2_clk.common.hw,
+ [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw,
+ [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_I2S0] = &i2s0_clk.common.hw,
+ [CLK_I2S1] = &i2s1_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_PHY1] = &usb_phy1_clk.common.hw,
+ [CLK_USB_HSIC] = &usb_hsic_clk.common.hw,
+ [CLK_USB_HSIC_12M] = &usb_hsic_12M_clk.common.hw,
+ [CLK_USB_OHCI] = &usb_ohci_clk.common.hw,
+ [CLK_DRAM_VE] = &dram_ve_clk.common.hw,
+ [CLK_DRAM_CSI] = &dram_csi_clk.common.hw,
+ [CLK_DRAM_DRC] = &dram_drc_clk.common.hw,
+ [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw,
+ [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw,
+ [CLK_DE_BE] = &de_be_clk.common.hw,
+ [CLK_DE_FE] = &de_fe_clk.common.hw,
+ [CLK_LCD_CH0] = &lcd_ch0_clk.common.hw,
+ [CLK_LCD_CH1] = &lcd_ch1_clk.common.hw,
+ [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw,
+ [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_AC_DIG] = &ac_dig_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_MBUS] = &mbus_clk.common.hw,
+ [CLK_DSI_SCLK] = &dsi_sclk_clk.common.hw,
+ [CLK_DSI_DPHY] = &dsi_dphy_clk.common.hw,
+ [CLK_DRC] = &drc_clk.common.hw,
+ [CLK_GPU] = &gpu_clk.common.hw,
+ [CLK_ATS] = &ats_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun8i_a23_ccu_resets[] = {
+ [RST_USB_PHY0] = { 0x0cc, BIT(0) },
+ [RST_USB_PHY1] = { 0x0cc, BIT(1) },
+ [RST_USB_HSIC] = { 0x0cc, BIT(2) },
+
+ [RST_MBUS] = { 0x0fc, BIT(31) },
+
+ [RST_BUS_MIPI_DSI] = { 0x2c0, BIT(1) },
+ [RST_BUS_DMA] = { 0x2c0, BIT(6) },
+ [RST_BUS_MMC0] = { 0x2c0, BIT(8) },
+ [RST_BUS_MMC1] = { 0x2c0, BIT(9) },
+ [RST_BUS_MMC2] = { 0x2c0, BIT(10) },
+ [RST_BUS_NAND] = { 0x2c0, BIT(13) },
+ [RST_BUS_DRAM] = { 0x2c0, BIT(14) },
+ [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) },
+ [RST_BUS_SPI0] = { 0x2c0, BIT(20) },
+ [RST_BUS_SPI1] = { 0x2c0, BIT(21) },
+ [RST_BUS_OTG] = { 0x2c0, BIT(24) },
+ [RST_BUS_EHCI] = { 0x2c0, BIT(26) },
+ [RST_BUS_OHCI] = { 0x2c0, BIT(29) },
+
+ [RST_BUS_VE] = { 0x2c4, BIT(0) },
+ [RST_BUS_LCD] = { 0x2c4, BIT(4) },
+ [RST_BUS_CSI] = { 0x2c4, BIT(8) },
+ [RST_BUS_DE_BE] = { 0x2c4, BIT(12) },
+ [RST_BUS_DE_FE] = { 0x2c4, BIT(14) },
+ [RST_BUS_GPU] = { 0x2c4, BIT(20) },
+ [RST_BUS_MSGBOX] = { 0x2c4, BIT(21) },
+ [RST_BUS_SPINLOCK] = { 0x2c4, BIT(22) },
+ [RST_BUS_DRC] = { 0x2c4, BIT(25) },
+
+ [RST_BUS_LVDS] = { 0x2c8, BIT(0) },
+
+ [RST_BUS_CODEC] = { 0x2d0, BIT(0) },
+ [RST_BUS_I2S0] = { 0x2d0, BIT(12) },
+ [RST_BUS_I2S1] = { 0x2d0, BIT(13) },
+
+ [RST_BUS_I2C0] = { 0x2d8, BIT(0) },
+ [RST_BUS_I2C1] = { 0x2d8, BIT(1) },
+ [RST_BUS_I2C2] = { 0x2d8, BIT(2) },
+ [RST_BUS_UART0] = { 0x2d8, BIT(16) },
+ [RST_BUS_UART1] = { 0x2d8, BIT(17) },
+ [RST_BUS_UART2] = { 0x2d8, BIT(18) },
+ [RST_BUS_UART3] = { 0x2d8, BIT(19) },
+ [RST_BUS_UART4] = { 0x2d8, BIT(20) },
+};
+
+static const struct sunxi_ccu_desc sun8i_a23_ccu_desc = {
+ .ccu_clks = sun8i_a23_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun8i_a23_ccu_clks),
+
+ .hw_clks = &sun8i_a23_hw_clks,
+
+ .resets = sun8i_a23_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun8i_a23_ccu_resets),
+};
+
+static void __init sun8i_a23_ccu_setup(struct device_node *node)
+{
+ void __iomem *reg;
+ u32 val;
+
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (IS_ERR(reg)) {
+ pr_err("%s: Could not map the clock registers\n",
+ of_node_full_name(node));
+ return;
+ }
+
+ /* Force the PLL-Audio-1x divider to 4 */
+ val = readl(reg + SUN8I_A23_PLL_AUDIO_REG);
+ val &= ~GENMASK(19, 16);
+ writel(val | (3 << 16), reg + SUN8I_A23_PLL_AUDIO_REG);
+
+ /* Force PLL-MIPI to MIPI mode */
+ val = readl(reg + SUN8I_A23_PLL_MIPI_REG);
+ val &= ~BIT(16);
+ writel(val, reg + SUN8I_A23_PLL_MIPI_REG);
+
+ sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc);
+}
+CLK_OF_DECLARE(sun8i_a23_ccu, "allwinner,sun8i-a23-ccu",
+ sun8i_a23_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
new file mode 100644
index 0000000..96b40ca
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
@@ -0,0 +1,780 @@
+/*
+ * Copyright (c) 2016 Maxime Ripard. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+
+#include "ccu_common.h"
+#include "ccu_reset.h"
+
+#include "ccu_div.h"
+#include "ccu_gate.h"
+#include "ccu_mp.h"
+#include "ccu_mult.h"
+#include "ccu_nk.h"
+#include "ccu_nkm.h"
+#include "ccu_nkmp.h"
+#include "ccu_nm.h"
+#include "ccu_phase.h"
+
+#include "ccu-sun8i-a23-a33.h"
+
+static struct ccu_nkmp pll_cpux_clk = {
+ .enable = BIT(31),
+ .lock = BIT(28),
+
+ .n = _SUNXI_CCU_MULT(8, 5),
+ .k = _SUNXI_CCU_MULT(4, 2),
+ .m = _SUNXI_CCU_DIV(0, 2),
+ .p = _SUNXI_CCU_DIV_MAX(16, 2, 4),
+
+ .common = {
+ .reg = 0x000,
+ .hw.init = CLK_HW_INIT("pll-cpux", "osc24M",
+ &ccu_nkmp_ops,
+ 0),
+ },
+};
+
+/*
+ * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from
+ * the base (2x, 4x and 8x), and one variable divider (the one true
+ * pll audio).
+ *
+ * We don't have any need for the variable divider for now, so we just
+ * hardcode it to match with the clock names
+ */
+#define SUN8I_A33_PLL_AUDIO_REG 0x008
+
+static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base",
+ "osc24M", 0x008,
+ 8, 7, /* N */
+ 0, 5, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video_clk, "pll-video",
+ "osc24M", 0x010,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve",
+ "osc24M", 0x018,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0",
+ "osc24M", 0x020,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ 0, 2, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 0);
+
+static SUNXI_CCU_NK_WITH_GATE_LOCK_POSTDIV(pll_periph_clk, "pll-periph",
+ "osc24M", 0x028,
+ 8, 5, /* N */
+ 4, 2, /* K */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ 2, /* post-div */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu",
+ "osc24M", 0x038,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+/*
+ * The MIPI PLL has 2 modes: "MIPI" and "HDMI".
+ *
+ * The MIPI mode is a standard NKM-style clock. The HDMI mode is an
+ * integer / fractional clock with switchable multipliers and dividers.
+ * This is not supported here. We hardcode the PLL to MIPI mode.
+ */
+#define SUN8I_A33_PLL_MIPI_REG 0x040
+static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_mipi_clk, "pll-mipi",
+ "pll-video", 0x040,
+ 8, 4, /* N */
+ 4, 2, /* K */
+ 0, 4, /* M */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_hsic_clk, "pll-hsic",
+ "osc24M", 0x044,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de",
+ "osc24M", 0x048,
+ 8, 7, /* N */
+ 0, 4, /* M */
+ BIT(24), /* frac enable */
+ BIT(25), /* frac select */
+ 270000000, /* frac rate 0 */
+ 297000000, /* frac rate 1 */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+/* TODO: Fix N */
+static SUNXI_CCU_N_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1",
+ "osc24M", 0x04c,
+ 8, 6, /* N */
+ BIT(31), /* gate */
+ BIT(28), /* lock */
+ CLK_SET_RATE_UNGATE);
+
+static const char * const cpux_parents[] = { "osc32k", "osc24M",
+ "pll-cpux" , "pll-cpux" };
+static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents,
+ 0x050, 16, 2, CLK_IS_CRITICAL);
+
+static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0);
+
+static const char * const ahb1_parents[] = { "osc32k", "osc24M",
+ "axi" , "pll-periph" };
+static struct ccu_div ahb1_clk = {
+ .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO),
+
+ .mux = {
+ .shift = 12,
+ .width = 2,
+
+ .variable_prediv = {
+ .index = 3,
+ .shift = 6,
+ .width = 2,
+ },
+ },
+
+ .common = {
+ .reg = 0x054,
+ .features = CCU_FEATURE_VARIABLE_PREDIV,
+ .hw.init = CLK_HW_INIT_PARENTS("ahb1",
+ ahb1_parents,
+ &ccu_div_ops,
+ 0),
+ },
+};
+
+static struct clk_div_table apb1_div_table[] = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 8 },
+ { /* Sentinel */ },
+};
+static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1",
+ 0x054, 8, 2, apb1_div_table, 0);
+
+static const char * const apb2_parents[] = { "osc32k", "osc24M",
+ "pll-periph" , "pll-periph" };
+static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058,
+ 0, 5, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ 0);
+
+static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb1",
+ 0x060, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_ss_clk, "bus-ss", "ahb1",
+ 0x060, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1",
+ 0x060, BIT(6), 0);
+static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1",
+ 0x060, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1",
+ 0x060, BIT(9), 0);
+static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1",
+ 0x060, BIT(10), 0);
+static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb1",
+ 0x060, BIT(13), 0);
+static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1",
+ 0x060, BIT(14), 0);
+static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1",
+ 0x060, BIT(19), 0);
+static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1",
+ 0x060, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb1",
+ 0x060, BIT(21), 0);
+static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1",
+ 0x060, BIT(24), 0);
+static SUNXI_CCU_GATE(bus_ehci_clk, "bus-ehci", "ahb1",
+ 0x060, BIT(26), 0);
+static SUNXI_CCU_GATE(bus_ohci_clk, "bus-ohci", "ahb1",
+ 0x060, BIT(29), 0);
+
+static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1",
+ 0x064, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_lcd_clk, "bus-lcd", "ahb1",
+ 0x064, BIT(4), 0);
+static SUNXI_CCU_GATE(bus_csi_clk, "bus-csi", "ahb1",
+ 0x064, BIT(8), 0);
+static SUNXI_CCU_GATE(bus_de_be_clk, "bus-de-be", "ahb1",
+ 0x064, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_de_fe_clk, "bus-de-fe", "ahb1",
+ 0x064, BIT(14), 0);
+static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "ahb1",
+ 0x064, BIT(20), 0);
+static SUNXI_CCU_GATE(bus_msgbox_clk, "bus-msgbox", "ahb1",
+ 0x064, BIT(21), 0);
+static SUNXI_CCU_GATE(bus_spinlock_clk, "bus-spinlock", "ahb1",
+ 0x064, BIT(22), 0);
+static SUNXI_CCU_GATE(bus_drc_clk, "bus-drc", "ahb1",
+ 0x064, BIT(25), 0);
+static SUNXI_CCU_GATE(bus_sat_clk, "bus-sat", "ahb1",
+ 0x064, BIT(26), 0);
+
+static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1",
+ 0x068, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1",
+ 0x068, BIT(5), 0);
+static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1",
+ 0x068, BIT(12), 0);
+static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1",
+ 0x068, BIT(13), 0);
+
+static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2",
+ 0x06c, BIT(0), 0);
+static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2",
+ 0x06c, BIT(1), 0);
+static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2",
+ 0x06c, BIT(2), 0);
+static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2",
+ 0x06c, BIT(16), 0);
+static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2",
+ 0x06c, BIT(17), 0);
+static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2",
+ 0x06c, BIT(18), 0);
+static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2",
+ 0x06c, BIT(19), 0);
+static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb2",
+ 0x06c, BIT(20), 0);
+
+static const char * const mod0_default_parents[] = { "osc24M", "pll-periph" };
+static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0",
+ 0x088, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0",
+ 0x088, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1",
+ 0x08c, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1",
+ 0x08c, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2",
+ 0x090, 20, 3, 0);
+static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2",
+ 0x090, 8, 3, 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_default_parents, 0x09c,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4,
+ 0, 4, /* M */
+ 16, 2, /* P */
+ 24, 2, /* mux */
+ BIT(31), /* gate */
+ 0);
+
+static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x",
+ "pll-audio-2x", "pll-audio" };
+static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents,
+ 0x0b0, 16, 2, BIT(31), 0);
+
+static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents,
+ 0x0b4, 16, 2, BIT(31), 0);
+
+/* TODO: the parent for most of the USB clocks is not known */
+static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M",
+ 0x0cc, BIT(8), 0);
+static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M",
+ 0x0cc, BIT(9), 0);
+static SUNXI_CCU_GATE(usb_hsic_clk, "usb-hsic", "pll-hsic",
+ 0x0cc, BIT(10), 0);
+static SUNXI_CCU_GATE(usb_hsic_12M_clk, "usb-hsic-12M", "osc24M",
+ 0x0cc, BIT(11), 0);
+static SUNXI_CCU_GATE(usb_ohci_clk, "usb-ohci", "osc24M",
+ 0x0cc, BIT(16), 0);
+
+static SUNXI_CCU_M(dram_clk, "dram", "pll-ddr",
+ 0x0f4, 0, 4, CLK_IS_CRITICAL);
+
+static const char * const pll_ddr_parents[] = { "pll-ddr0", "pll-ddr1" };
+static SUNXI_CCU_MUX(pll_ddr_clk, "pll-ddr", pll_ddr_parents,
+ 0x0f8, 16, 1, 0);
+
+static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "dram",
+ 0x100, BIT(0), 0);
+static SUNXI_CCU_GATE(dram_csi_clk, "dram-csi", "dram",
+ 0x100, BIT(1), 0);
+static SUNXI_CCU_GATE(dram_drc_clk, "dram-drc", "dram",
+ 0x100, BIT(16), 0);
+static SUNXI_CCU_GATE(dram_de_fe_clk, "dram-de-fe", "dram",
+ 0x100, BIT(24), 0);
+static SUNXI_CCU_GATE(dram_de_be_clk, "dram-de-be", "dram",
+ 0x100, BIT(26), 0);
+
+static const char * const de_parents[] = { "pll-video", "pll-periph-2x",
+ "pll-gpu", "pll-de" };
+static const u8 de_table[] = { 0, 2, 3, 5 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(de_be_clk, "de-be",
+ de_parents, de_table,
+ 0x104, 0, 4, 24, 3, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(de_fe_clk, "de-fe",
+ de_parents, de_table,
+ 0x10c, 0, 4, 24, 3, BIT(31), 0);
+
+static const char * const lcd_ch0_parents[] = { "pll-video", "pll-video-2x",
+ "pll-mipi" };
+static const u8 lcd_ch0_table[] = { 0, 2, 4 };
+static SUNXI_CCU_MUX_TABLE_WITH_GATE(lcd_ch0_clk, "lcd-ch0",
+ lcd_ch0_parents, lcd_ch0_table,
+ 0x118, 24, 3, BIT(31),
+ CLK_SET_RATE_PARENT);
+
+static const char * const lcd_ch1_parents[] = { "pll-video", "pll-video-2x" };
+static const u8 lcd_ch1_table[] = { 0, 2 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(lcd_ch1_clk, "lcd-ch1",
+ lcd_ch1_parents, lcd_ch1_table,
+ 0x12c, 0, 4, 24, 2, BIT(31), 0);
+
+static const char * const csi_sclk_parents[] = { "pll-video", "pll-de",
+ "pll-mipi", "pll-ve" };
+static const u8 csi_sclk_table[] = { 0, 3, 4, 5 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_sclk_clk, "csi-sclk",
+ csi_sclk_parents, csi_sclk_table,
+ 0x134, 16, 4, 24, 3, BIT(31), 0);
+
+static const char * const csi_mclk_parents[] = { "pll-video", "pll-de",
+ "osc24M" };
+static const u8 csi_mclk_table[] = { 0, 3, 5 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi_mclk_clk, "csi-mclk",
+ csi_mclk_parents, csi_mclk_table,
+ 0x134, 0, 5, 8, 3, BIT(15), 0);
+
+static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve",
+ 0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT);
+
+static SUNXI_CCU_GATE(ac_dig_clk, "ac-dig", "pll-audio",
+ 0x140, BIT(31), 0);
+static SUNXI_CCU_GATE(ac_dig_4x_clk, "ac-dig-4x", "pll-audio-4x",
+ 0x140, BIT(30), 0);
+static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M",
+ 0x144, BIT(31), 0);
+
+static const char * const mbus_parents[] = { "osc24M", "pll-periph-2x",
+ "pll-ddr0", "pll-ddr1" };
+static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents,
+ 0x15c, 0, 3, 24, 2, BIT(31), CLK_IS_CRITICAL);
+
+static const char * const dsi_sclk_parents[] = { "pll-video", "pll-video-2x" };
+static const u8 dsi_sclk_table[] = { 0, 2 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_sclk_clk, "dsi-sclk",
+ dsi_sclk_parents, dsi_sclk_table,
+ 0x168, 16, 4, 24, 2, BIT(31), 0);
+
+static const char * const dsi_dphy_parents[] = { "pll-video", "pll-periph" };
+static const u8 dsi_dphy_table[] = { 0, 2 };
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy",
+ dsi_dphy_parents, dsi_dphy_table,
+ 0x168, 0, 4, 8, 2, BIT(15), 0);
+
+static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(drc_clk, "drc",
+ de_parents, de_table,
+ 0x180, 0, 4, 24, 3, BIT(31), 0);
+
+static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu",
+ 0x1a0, 0, 3, BIT(31), 0);
+
+static const char * const ats_parents[] = { "osc24M", "pll-periph" };
+static SUNXI_CCU_M_WITH_MUX_GATE(ats_clk, "ats", ats_parents,
+ 0x1b0, 0, 3, 24, 2, BIT(31), 0);
+
+static struct ccu_common *sun8i_a33_ccu_clks[] = {
+ &pll_cpux_clk.common,
+ &pll_audio_base_clk.common,
+ &pll_video_clk.common,
+ &pll_ve_clk.common,
+ &pll_ddr0_clk.common,
+ &pll_periph_clk.common,
+ &pll_gpu_clk.common,
+ &pll_mipi_clk.common,
+ &pll_hsic_clk.common,
+ &pll_de_clk.common,
+ &pll_ddr1_clk.common,
+ &pll_ddr_clk.common,
+ &cpux_clk.common,
+ &axi_clk.common,
+ &ahb1_clk.common,
+ &apb1_clk.common,
+ &apb2_clk.common,
+ &bus_mipi_dsi_clk.common,
+ &bus_ss_clk.common,
+ &bus_dma_clk.common,
+ &bus_mmc0_clk.common,
+ &bus_mmc1_clk.common,
+ &bus_mmc2_clk.common,
+ &bus_nand_clk.common,
+ &bus_dram_clk.common,
+ &bus_hstimer_clk.common,
+ &bus_spi0_clk.common,
+ &bus_spi1_clk.common,
+ &bus_otg_clk.common,
+ &bus_ehci_clk.common,
+ &bus_ohci_clk.common,
+ &bus_ve_clk.common,
+ &bus_lcd_clk.common,
+ &bus_csi_clk.common,
+ &bus_de_fe_clk.common,
+ &bus_de_be_clk.common,
+ &bus_gpu_clk.common,
+ &bus_msgbox_clk.common,
+ &bus_spinlock_clk.common,
+ &bus_drc_clk.common,
+ &bus_sat_clk.common,
+ &bus_codec_clk.common,
+ &bus_pio_clk.common,
+ &bus_i2s0_clk.common,
+ &bus_i2s1_clk.common,
+ &bus_i2c0_clk.common,
+ &bus_i2c1_clk.common,
+ &bus_i2c2_clk.common,
+ &bus_uart0_clk.common,
+ &bus_uart1_clk.common,
+ &bus_uart2_clk.common,
+ &bus_uart3_clk.common,
+ &bus_uart4_clk.common,
+ &nand_clk.common,
+ &mmc0_clk.common,
+ &mmc0_sample_clk.common,
+ &mmc0_output_clk.common,
+ &mmc1_clk.common,
+ &mmc1_sample_clk.common,
+ &mmc1_output_clk.common,
+ &mmc2_clk.common,
+ &mmc2_sample_clk.common,
+ &mmc2_output_clk.common,
+ &ss_clk.common,
+ &spi0_clk.common,
+ &spi1_clk.common,
+ &i2s0_clk.common,
+ &i2s1_clk.common,
+ &usb_phy0_clk.common,
+ &usb_phy1_clk.common,
+ &usb_hsic_clk.common,
+ &usb_hsic_12M_clk.common,
+ &usb_ohci_clk.common,
+ &dram_clk.common,
+ &dram_ve_clk.common,
+ &dram_csi_clk.common,
+ &dram_drc_clk.common,
+ &dram_de_fe_clk.common,
+ &dram_de_be_clk.common,
+ &de_be_clk.common,
+ &de_fe_clk.common,
+ &lcd_ch0_clk.common,
+ &lcd_ch1_clk.common,
+ &csi_sclk_clk.common,
+ &csi_mclk_clk.common,
+ &ve_clk.common,
+ &ac_dig_clk.common,
+ &ac_dig_4x_clk.common,
+ &avs_clk.common,
+ &mbus_clk.common,
+ &dsi_sclk_clk.common,
+ &dsi_dphy_clk.common,
+ &drc_clk.common,
+ &gpu_clk.common,
+ &ats_clk.common,
+};
+
+/* We hardcode the divider to 4 for now */
+static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio",
+ "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x",
+ "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x",
+ "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x",
+ "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT);
+static CLK_FIXED_FACTOR(pll_periph_2x_clk, "pll-periph-2x",
+ "pll-periph", 1, 2, 0);
+static CLK_FIXED_FACTOR(pll_video_2x_clk, "pll-video-2x",
+ "pll-video", 1, 2, 0);
+
+static struct clk_hw_onecell_data sun8i_a33_hw_clks = {
+ .hws = {
+ [CLK_PLL_CPUX] = &pll_cpux_clk.common.hw,
+ [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw,
+ [CLK_PLL_AUDIO] = &pll_audio_clk.hw,
+ [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw,
+ [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw,
+ [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw,
+ [CLK_PLL_VIDEO] = &pll_video_clk.common.hw,
+ [CLK_PLL_VIDEO_2X] = &pll_video_2x_clk.hw,
+ [CLK_PLL_VE] = &pll_ve_clk.common.hw,
+ [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw,
+ [CLK_PLL_PERIPH] = &pll_periph_clk.common.hw,
+ [CLK_PLL_PERIPH_2X] = &pll_periph_2x_clk.hw,
+ [CLK_PLL_GPU] = &pll_gpu_clk.common.hw,
+ [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw,
+ [CLK_PLL_HSIC] = &pll_hsic_clk.common.hw,
+ [CLK_PLL_DE] = &pll_de_clk.common.hw,
+ [CLK_PLL_DDR1] = &pll_ddr1_clk.common.hw,
+ [CLK_PLL_DDR] = &pll_ddr_clk.common.hw,
+ [CLK_CPUX] = &cpux_clk.common.hw,
+ [CLK_AXI] = &axi_clk.common.hw,
+ [CLK_AHB1] = &ahb1_clk.common.hw,
+ [CLK_APB1] = &apb1_clk.common.hw,
+ [CLK_APB2] = &apb2_clk.common.hw,
+ [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw,
+ [CLK_BUS_SS] = &bus_ss_clk.common.hw,
+ [CLK_BUS_DMA] = &bus_dma_clk.common.hw,
+ [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw,
+ [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw,
+ [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw,
+ [CLK_BUS_NAND] = &bus_nand_clk.common.hw,
+ [CLK_BUS_DRAM] = &bus_dram_clk.common.hw,
+ [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw,
+ [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw,
+ [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw,
+ [CLK_BUS_OTG] = &bus_otg_clk.common.hw,
+ [CLK_BUS_EHCI] = &bus_ehci_clk.common.hw,
+ [CLK_BUS_OHCI] = &bus_ohci_clk.common.hw,
+ [CLK_BUS_VE] = &bus_ve_clk.common.hw,
+ [CLK_BUS_LCD] = &bus_lcd_clk.common.hw,
+ [CLK_BUS_CSI] = &bus_csi_clk.common.hw,
+ [CLK_BUS_DE_BE] = &bus_de_be_clk.common.hw,
+ [CLK_BUS_DE_FE] = &bus_de_fe_clk.common.hw,
+ [CLK_BUS_GPU] = &bus_gpu_clk.common.hw,
+ [CLK_BUS_MSGBOX] = &bus_msgbox_clk.common.hw,
+ [CLK_BUS_SPINLOCK] = &bus_spinlock_clk.common.hw,
+ [CLK_BUS_DRC] = &bus_drc_clk.common.hw,
+ [CLK_BUS_SAT] = &bus_sat_clk.common.hw,
+ [CLK_BUS_CODEC] = &bus_codec_clk.common.hw,
+ [CLK_BUS_PIO] = &bus_pio_clk.common.hw,
+ [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw,
+ [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw,
+ [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw,
+ [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw,
+ [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw,
+ [CLK_BUS_UART0] = &bus_uart0_clk.common.hw,
+ [CLK_BUS_UART1] = &bus_uart1_clk.common.hw,
+ [CLK_BUS_UART2] = &bus_uart2_clk.common.hw,
+ [CLK_BUS_UART3] = &bus_uart3_clk.common.hw,
+ [CLK_BUS_UART4] = &bus_uart4_clk.common.hw,
+ [CLK_NAND] = &nand_clk.common.hw,
+ [CLK_MMC0] = &mmc0_clk.common.hw,
+ [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw,
+ [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw,
+ [CLK_MMC1] = &mmc1_clk.common.hw,
+ [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw,
+ [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw,
+ [CLK_MMC2] = &mmc2_clk.common.hw,
+ [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw,
+ [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw,
+ [CLK_SS] = &ss_clk.common.hw,
+ [CLK_SPI0] = &spi0_clk.common.hw,
+ [CLK_SPI1] = &spi1_clk.common.hw,
+ [CLK_I2S0] = &i2s0_clk.common.hw,
+ [CLK_I2S1] = &i2s1_clk.common.hw,
+ [CLK_USB_PHY0] = &usb_phy0_clk.common.hw,
+ [CLK_USB_PHY1] = &usb_phy1_clk.common.hw,
+ [CLK_USB_HSIC] = &usb_hsic_clk.common.hw,
+ [CLK_USB_HSIC_12M] = &usb_hsic_12M_clk.common.hw,
+ [CLK_USB_OHCI] = &usb_ohci_clk.common.hw,
+ [CLK_DRAM] = &dram_clk.common.hw,
+ [CLK_DRAM_VE] = &dram_ve_clk.common.hw,
+ [CLK_DRAM_CSI] = &dram_csi_clk.common.hw,
+ [CLK_DRAM_DRC] = &dram_drc_clk.common.hw,
+ [CLK_DRAM_DE_FE] = &dram_de_fe_clk.common.hw,
+ [CLK_DRAM_DE_BE] = &dram_de_be_clk.common.hw,
+ [CLK_DE_BE] = &de_be_clk.common.hw,
+ [CLK_DE_FE] = &de_fe_clk.common.hw,
+ [CLK_LCD_CH0] = &lcd_ch0_clk.common.hw,
+ [CLK_LCD_CH1] = &lcd_ch1_clk.common.hw,
+ [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw,
+ [CLK_CSI_MCLK] = &csi_mclk_clk.common.hw,
+ [CLK_VE] = &ve_clk.common.hw,
+ [CLK_AC_DIG] = &ac_dig_clk.common.hw,
+ [CLK_AC_DIG_4X] = &ac_dig_4x_clk.common.hw,
+ [CLK_AVS] = &avs_clk.common.hw,
+ [CLK_MBUS] = &mbus_clk.common.hw,
+ [CLK_DSI_SCLK] = &dsi_sclk_clk.common.hw,
+ [CLK_DSI_DPHY] = &dsi_dphy_clk.common.hw,
+ [CLK_DRC] = &drc_clk.common.hw,
+ [CLK_GPU] = &gpu_clk.common.hw,
+ [CLK_ATS] = &ats_clk.common.hw,
+ },
+ .num = CLK_NUMBER,
+};
+
+static struct ccu_reset_map sun8i_a33_ccu_resets[] = {
+ [RST_USB_PHY0] = { 0x0cc, BIT(0) },
+ [RST_USB_PHY1] = { 0x0cc, BIT(1) },
+ [RST_USB_HSIC] = { 0x0cc, BIT(2) },
+
+ [RST_MBUS] = { 0x0fc, BIT(31) },
+
+ [RST_BUS_MIPI_DSI] = { 0x2c0, BIT(1) },
+ [RST_BUS_SS] = { 0x2c0, BIT(5) },
+ [RST_BUS_DMA] = { 0x2c0, BIT(6) },
+ [RST_BUS_MMC0] = { 0x2c0, BIT(8) },
+ [RST_BUS_MMC1] = { 0x2c0, BIT(9) },
+ [RST_BUS_MMC2] = { 0x2c0, BIT(10) },
+ [RST_BUS_NAND] = { 0x2c0, BIT(13) },
+ [RST_BUS_DRAM] = { 0x2c0, BIT(14) },
+ [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) },
+ [RST_BUS_SPI0] = { 0x2c0, BIT(20) },
+ [RST_BUS_SPI1] = { 0x2c0, BIT(21) },
+ [RST_BUS_OTG] = { 0x2c0, BIT(24) },
+ [RST_BUS_EHCI] = { 0x2c0, BIT(26) },
+ [RST_BUS_OHCI] = { 0x2c0, BIT(29) },
+
+ [RST_BUS_VE] = { 0x2c4, BIT(0) },
+ [RST_BUS_LCD] = { 0x2c4, BIT(4) },
+ [RST_BUS_CSI] = { 0x2c4, BIT(8) },
+ [RST_BUS_DE_BE] = { 0x2c4, BIT(12) },
+ [RST_BUS_DE_FE] = { 0x2c4, BIT(14) },
+ [RST_BUS_GPU] = { 0x2c4, BIT(20) },
+ [RST_BUS_MSGBOX] = { 0x2c4, BIT(21) },
+ [RST_BUS_SPINLOCK] = { 0x2c4, BIT(22) },
+ [RST_BUS_DRC] = { 0x2c4, BIT(25) },
+ [RST_BUS_SAT] = { 0x2c4, BIT(26) },
+
+ [RST_BUS_LVDS] = { 0x2c8, BIT(0) },
+
+ [RST_BUS_CODEC] = { 0x2d0, BIT(0) },
+ [RST_BUS_I2S0] = { 0x2d0, BIT(12) },
+ [RST_BUS_I2S1] = { 0x2d0, BIT(13) },
+
+ [RST_BUS_I2C0] = { 0x2d8, BIT(0) },
+ [RST_BUS_I2C1] = { 0x2d8, BIT(1) },
+ [RST_BUS_I2C2] = { 0x2d8, BIT(2) },
+ [RST_BUS_UART0] = { 0x2d8, BIT(16) },
+ [RST_BUS_UART1] = { 0x2d8, BIT(17) },
+ [RST_BUS_UART2] = { 0x2d8, BIT(18) },
+ [RST_BUS_UART3] = { 0x2d8, BIT(19) },
+ [RST_BUS_UART4] = { 0x2d8, BIT(20) },
+};
+
+static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = {
+ .ccu_clks = sun8i_a33_ccu_clks,
+ .num_ccu_clks = ARRAY_SIZE(sun8i_a33_ccu_clks),
+
+ .hw_clks = &sun8i_a33_hw_clks,
+
+ .resets = sun8i_a33_ccu_resets,
+ .num_resets = ARRAY_SIZE(sun8i_a33_ccu_resets),
+};
+
+static void __init sun8i_a33_ccu_setup(struct device_node *node)
+{
+ void __iomem *reg;
+ u32 val;
+
+ reg = of_io_request_and_map(node, 0, of_node_full_name(node));
+ if (IS_ERR(reg)) {
+ pr_err("%s: Could not map the clock registers\n",
+ of_node_full_name(node));
+ return;
+ }
+
+ /* Force the PLL-Audio-1x divider to 4 */
+ val = readl(reg + SUN8I_A33_PLL_AUDIO_REG);
+ val &= ~GENMASK(19, 16);
+ writel(val | (3 << 16), reg + SUN8I_A33_PLL_AUDIO_REG);
+
+ /* Force PLL-MIPI to MIPI mode */
+ val = readl(reg + SUN8I_A33_PLL_MIPI_REG);
+ val &= ~BIT(16);
+ writel(val, reg + SUN8I_A33_PLL_MIPI_REG);
+
+ sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
+}
+CLK_OF_DECLARE(sun8i_a33_ccu, "allwinner,sun8i-a33-ccu",
+ sun8i_a33_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 267f995..4d70590 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -184,15 +184,15 @@ static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058,
0);
static const char * const ahb2_parents[] = { "ahb1" , "pll-periph0" };
+static const struct ccu_mux_fixed_prediv ahb2_fixed_predivs[] = {
+ { .index = 1, .div = 2 },
+};
static struct ccu_mux ahb2_clk = {
.mux = {
.shift = 0,
.width = 1,
-
- .fixed_prediv = {
- .index = 1,
- .div = 2,
- },
+ .fixed_predivs = ahb2_fixed_predivs,
+ .n_predivs = ARRAY_SIZE(ahb2_fixed_predivs),
},
.common = {
diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h
index 653ade5..34c3388 100644
--- a/drivers/clk/sunxi-ng/ccu_div.h
+++ b/drivers/clk/sunxi-ng/ccu_div.h
@@ -19,10 +19,29 @@
#include "ccu_common.h"
#include "ccu_mux.h"
+/**
+ * struct _ccu_div - Internal divider description
+ * @shift: Bit offset of the divider in its register
+ * @width: Width of the divider field in its register
+ * @max: Maximum value allowed for that divider. This is the
+ * arithmetic value, not the maximum value to be set in the
+ * register.
+ * @flags: clk_divider flags to apply on this divider
+ * @table: Divider table pointer (if applicable)
+ *
+ * That structure represents a single divider, and is meant to be
+ * embedded in other structures representing the various clock
+ * classes.
+ *
+ * It is basically a wrapper around the clk_divider functions
+ * arguments.
+ */
struct _ccu_div {
u8 shift;
u8 width;
+ u32 max;
+
u32 flags;
struct clk_div_table *table;
@@ -36,14 +55,25 @@ struct _ccu_div {
.table = _table, \
}
-#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \
- _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, _flags)
-
#define _SUNXI_CCU_DIV_TABLE(_shift, _width, _table) \
_SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, _table, 0)
+#define _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, _flags) \
+ { \
+ .shift = _shift, \
+ .width = _width, \
+ .flags = _flags, \
+ .max = _max, \
+ }
+
+#define _SUNXI_CCU_DIV_FLAGS(_shift, _width, _flags) \
+ _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, 0, _flags)
+
+#define _SUNXI_CCU_DIV_MAX(_shift, _width, _max) \
+ _SUNXI_CCU_DIV_MAX_FLAGS(_shift, _width, _max, 0)
+
#define _SUNXI_CCU_DIV(_shift, _width) \
- _SUNXI_CCU_DIV_TABLE_FLAGS(_shift, _width, NULL, 0)
+ _SUNXI_CCU_DIV_FLAGS(_shift, _width, 0)
struct ccu_div {
u32 enable;
@@ -77,13 +107,16 @@ struct ccu_div {
_shift, _width, _table, 0, \
_flags)
-#define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
- _mshift, _mwidth, _muxshift, _muxwidth, \
- _gate, _flags) \
+#define SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name, \
+ _parents, _table, \
+ _reg, \
+ _mshift, _mwidth, \
+ _muxshift, _muxwidth, \
+ _gate, _flags) \
struct ccu_div _struct = { \
.enable = _gate, \
.div = _SUNXI_CCU_DIV(_mshift, _mwidth), \
- .mux = SUNXI_CLK_MUX(_muxshift, _muxwidth), \
+ .mux = _SUNXI_CCU_MUX_TABLE(_muxshift, _muxwidth, _table), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
@@ -93,12 +126,23 @@ struct ccu_div {
}, \
}
+#define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
+ _mshift, _mwidth, _muxshift, _muxwidth, \
+ _gate, _flags) \
+ SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name, \
+ _parents, NULL, \
+ _reg, _mshift, _mwidth, \
+ _muxshift, _muxwidth, \
+ _gate, _flags)
+
#define SUNXI_CCU_M_WITH_MUX(_struct, _name, _parents, _reg, \
_mshift, _mwidth, _muxshift, _muxwidth, \
_flags) \
- SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
- _mshift, _mwidth, _muxshift, _muxwidth, \
- 0, _flags)
+ SUNXI_CCU_M_WITH_MUX_TABLE_GATE(_struct, _name, \
+ _parents, NULL, \
+ _reg, _mshift, _mwidth, \
+ _muxshift, _muxwidth, \
+ 0, _flags)
#define SUNXI_CCU_M_WITH_GATE(_struct, _name, _parent, _reg, \
diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c
index cbf33ef..ebb1b31 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.c
+++ b/drivers/clk/sunxi-ng/ccu_mp.c
@@ -21,9 +21,9 @@ static void ccu_mp_find_best(unsigned long parent, unsigned long rate,
unsigned int best_m = 0, best_p = 0;
unsigned int _m, _p;
- for (_p = 0; _p <= max_p; _p++) {
+ for (_p = 1; _p <= max_p; _p <<= 1) {
for (_m = 1; _m <= max_m; _m++) {
- unsigned long tmp_rate = (parent >> _p) / _m;
+ unsigned long tmp_rate = parent / _p / _m;
if (tmp_rate > rate)
continue;
@@ -46,13 +46,15 @@ static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux,
void *data)
{
struct ccu_mp *cmp = data;
+ unsigned int max_m, max_p;
unsigned int m, p;
- ccu_mp_find_best(parent_rate, rate,
- 1 << cmp->m.width, (1 << cmp->p.width) - 1,
- &m, &p);
+ max_m = cmp->m.max ?: 1 << cmp->m.width;
+ max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
- return (parent_rate >> p) / m;
+ ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p);
+
+ return parent_rate / p / m;
}
static void ccu_mp_disable(struct clk_hw *hw)
@@ -108,13 +110,14 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct ccu_mp *cmp = hw_to_ccu_mp(hw);
unsigned long flags;
+ unsigned int max_m, max_p;
unsigned int m, p;
u32 reg;
- ccu_mp_find_best(parent_rate, rate,
- 1 << cmp->m.width, (1 << cmp->p.width) - 1,
- &m, &p);
+ max_m = cmp->m.max ?: 1 << cmp->m.width;
+ max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
+ ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p);
spin_lock_irqsave(cmp->common.lock, flags);
@@ -122,7 +125,7 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
reg &= ~GENMASK(cmp->m.width + cmp->m.shift - 1, cmp->m.shift);
reg &= ~GENMASK(cmp->p.width + cmp->p.shift - 1, cmp->p.shift);
- writel(reg | (p << cmp->p.shift) | ((m - 1) << cmp->m.shift),
+ writel(reg | (ilog2(p) << cmp->p.shift) | ((m - 1) << cmp->m.shift),
cmp->common.base + cmp->common.reg);
spin_unlock_irqrestore(cmp->common.lock, flags);
diff --git a/drivers/clk/sunxi-ng/ccu_mp.h b/drivers/clk/sunxi-ng/ccu_mp.h
index 3cf12bf..edf9215 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.h
+++ b/drivers/clk/sunxi-ng/ccu_mp.h
@@ -44,7 +44,7 @@ struct ccu_mp {
.enable = _gate, \
.m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
.p = _SUNXI_CCU_DIV(_pshift, _pwidth), \
- .mux = SUNXI_CLK_MUX(_muxshift, _muxwidth), \
+ .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
new file mode 100644
index 0000000..010e942
--- /dev/null
+++ b/drivers/clk/sunxi-ng/ccu_mult.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 Maxime Ripard
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk-provider.h>
+
+#include "ccu_gate.h"
+#include "ccu_mult.h"
+
+static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
+ unsigned int max_n, unsigned int *n)
+{
+ *n = rate / parent;
+}
+
+static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
+ unsigned long parent_rate,
+ unsigned long rate,
+ void *data)
+{
+ struct ccu_mult *cm = data;
+ unsigned int n;
+
+ ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n);
+
+ return parent_rate * n;
+}
+
+static void ccu_mult_disable(struct clk_hw *hw)
+{
+ struct ccu_mult *cm = hw_to_ccu_mult(hw);
+
+ return ccu_gate_helper_disable(&cm->common, cm->enable);
+}
+
+static int ccu_mult_enable(struct clk_hw *hw)
+{
+ struct ccu_mult *cm = hw_to_ccu_mult(hw);
+
+ return ccu_gate_helper_enable(&cm->common, cm->enable);
+}
+
+static int ccu_mult_is_enabled(struct clk_hw *hw)
+{
+ struct ccu_mult *cm = hw_to_ccu_mult(hw);
+
+ return ccu_gate_helper_is_enabled(&cm->common, cm->enable);
+}
+
+static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ccu_mult *cm = hw_to_ccu_mult(hw);
+ unsigned long val;
+ u32 reg;
+
+ reg = readl(cm->common.base + cm->common.reg);
+ val = reg >> cm->mult.shift;
+ val &= (1 << cm->mult.width) - 1;
+
+ ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
+ &parent_rate);
+
+ return parent_rate * (val + 1);
+}
+
+static int ccu_mult_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ccu_mult *cm = hw_to_ccu_mult(hw);
+
+ return ccu_mux_helper_determine_rate(&cm->common, &cm->mux,
+ req, ccu_mult_round_rate, cm);
+}
+
+static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ccu_mult *cm = hw_to_ccu_mult(hw);
+ unsigned long flags;
+ unsigned int n;
+ u32 reg;
+
+ ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
+ &parent_rate);
+
+ ccu_mult_find_best(parent_rate, rate, 1 << cm->mult.width, &n);
+
+ spin_lock_irqsave(cm->common.lock, flags);
+
+ reg = readl(cm->common.base + cm->common.reg);
+ reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift);
+
+ writel(reg | ((n - 1) << cm->mult.shift),
+ cm->common.base + cm->common.reg);
+
+ spin_unlock_irqrestore(cm->common.lock, flags);
+
+ return 0;
+}
+
+static u8 ccu_mult_get_parent(struct clk_hw *hw)
+{
+ struct ccu_mult *cm = hw_to_ccu_mult(hw);
+
+ return ccu_mux_helper_get_parent(&cm->common, &cm->mux);
+}
+
+static int ccu_mult_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct ccu_mult *cm = hw_to_ccu_mult(hw);
+
+ return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index);
+}
+
+const struct clk_ops ccu_mult_ops = {
+ .disable = ccu_mult_disable,
+ .enable = ccu_mult_enable,
+ .is_enabled = ccu_mult_is_enabled,
+
+ .get_parent = ccu_mult_get_parent,
+ .set_parent = ccu_mult_set_parent,
+
+ .determine_rate = ccu_mult_determine_rate,
+ .recalc_rate = ccu_mult_recalc_rate,
+ .set_rate = ccu_mult_set_rate,
+};
diff --git a/drivers/clk/sunxi-ng/ccu_mult.h b/drivers/clk/sunxi-ng/ccu_mult.h
index 609db66..5d2c8dc 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.h
+++ b/drivers/clk/sunxi-ng/ccu_mult.h
@@ -1,6 +1,9 @@
#ifndef _CCU_MULT_H_
#define _CCU_MULT_H_
+#include "ccu_common.h"
+#include "ccu_mux.h"
+
struct _ccu_mult {
u8 shift;
u8 width;
@@ -12,4 +15,36 @@ struct _ccu_mult {
.width = _width, \
}
+struct ccu_mult {
+ u32 enable;
+
+ struct _ccu_mult mult;
+ struct ccu_mux_internal mux;
+ struct ccu_common common;
+};
+
+#define SUNXI_CCU_N_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \
+ _mshift, _mwidth, _gate, _lock, \
+ _flags) \
+ struct ccu_mult _struct = { \
+ .enable = _gate, \
+ .mult = _SUNXI_CCU_MULT(_mshift, _mwidth), \
+ .common = { \
+ .reg = _reg, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &ccu_mult_ops, \
+ _flags), \
+ }, \
+ }
+
+static inline struct ccu_mult *hw_to_ccu_mult(struct clk_hw *hw)
+{
+ struct ccu_common *common = hw_to_ccu_common(hw);
+
+ return container_of(common, struct ccu_mult, common);
+}
+
+extern const struct clk_ops ccu_mult_ops;
+
#endif /* _CCU_MULT_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index 58fc36e..a43ad52 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -8,7 +8,9 @@
* the License, or (at your option) any later version.
*/
+#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/delay.h>
#include "ccu_gate.h"
#include "ccu_mux.h"
@@ -18,8 +20,9 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
int parent_index,
unsigned long *parent_rate)
{
- u8 prediv = 1;
+ u16 prediv = 1;
u32 reg;
+ int i;
if (!((common->features & CCU_FEATURE_FIXED_PREDIV) ||
(common->features & CCU_FEATURE_VARIABLE_PREDIV)))
@@ -32,8 +35,9 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
}
if (common->features & CCU_FEATURE_FIXED_PREDIV)
- if (parent_index == cm->fixed_prediv.index)
- prediv = cm->fixed_prediv.div;
+ for (i = 0; i < cm->n_predivs; i++)
+ if (parent_index == cm->fixed_predivs[i].index)
+ prediv = cm->fixed_predivs[i].div;
if (common->features & CCU_FEATURE_VARIABLE_PREDIV)
if (parent_index == cm->variable_prediv.index) {
@@ -107,6 +111,15 @@ u8 ccu_mux_helper_get_parent(struct ccu_common *common,
parent = reg >> cm->shift;
parent &= (1 << cm->width) - 1;
+ if (cm->table) {
+ int num_parents = clk_hw_get_num_parents(&common->hw);
+ int i;
+
+ for (i = 0; i < num_parents; i++)
+ if (cm->table[i] == parent)
+ return i;
+ }
+
return parent;
}
@@ -117,6 +130,9 @@ int ccu_mux_helper_set_parent(struct ccu_common *common,
unsigned long flags;
u32 reg;
+ if (cm->table)
+ index = cm->table[index];
+
spin_lock_irqsave(common->lock, flags);
reg = readl(common->base + common->reg);
@@ -185,3 +201,37 @@ const struct clk_ops ccu_mux_ops = {
.determine_rate = __clk_mux_determine_rate,
.recalc_rate = ccu_mux_recalc_rate,
};
+
+/*
+ * This clock notifier is called when the frequency of the of the parent
+ * PLL clock is to be changed. The idea is to switch the parent to a
+ * stable clock, such as the main oscillator, while the PLL frequency
+ * stabilizes.
+ */
+static int ccu_mux_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct ccu_mux_nb *mux = to_ccu_mux_nb(nb);
+ int ret = 0;
+
+ if (event == PRE_RATE_CHANGE) {
+ mux->original_index = ccu_mux_helper_get_parent(mux->common,
+ mux->cm);
+ ret = ccu_mux_helper_set_parent(mux->common, mux->cm,
+ mux->bypass_index);
+ } else if (event == POST_RATE_CHANGE) {
+ ret = ccu_mux_helper_set_parent(mux->common, mux->cm,
+ mux->original_index);
+ }
+
+ udelay(mux->delay_us);
+
+ return notifier_from_errno(ret);
+}
+
+int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb)
+{
+ mux_nb->clk_nb.notifier_call = ccu_mux_notifier_cb;
+
+ return clk_notifier_register(clk, &mux_nb->clk_nb);
+}
diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h
index 9450826..47aba3a 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.h
+++ b/drivers/clk/sunxi-ng/ccu_mux.h
@@ -5,14 +5,18 @@
#include "ccu_common.h"
+struct ccu_mux_fixed_prediv {
+ u8 index;
+ u16 div;
+};
+
struct ccu_mux_internal {
- u8 shift;
- u8 width;
+ u8 shift;
+ u8 width;
+ const u8 *table;
- struct {
- u8 index;
- u8 div;
- } fixed_prediv;
+ const struct ccu_mux_fixed_prediv *fixed_predivs;
+ u8 n_predivs;
struct {
u8 index;
@@ -21,12 +25,16 @@ struct ccu_mux_internal {
} variable_prediv;
};
-#define SUNXI_CLK_MUX(_shift, _width) \
- { \
- .shift = _shift, \
- .width = _width, \
+#define _SUNXI_CCU_MUX_TABLE(_shift, _width, _table) \
+ { \
+ .shift = _shift, \
+ .width = _width, \
+ .table = _table, \
}
+#define _SUNXI_CCU_MUX(_shift, _width) \
+ _SUNXI_CCU_MUX_TABLE(_shift, _width, NULL)
+
struct ccu_mux {
u16 reg;
u32 enable;
@@ -35,9 +43,12 @@ struct ccu_mux {
struct ccu_common common;
};
-#define SUNXI_CCU_MUX(_struct, _name, _parents, _reg, _shift, _width, _flags) \
+#define SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, _table, \
+ _reg, _shift, _width, _gate, \
+ _flags) \
struct ccu_mux _struct = { \
- .mux = SUNXI_CLK_MUX(_shift, _width), \
+ .enable = _gate, \
+ .mux = _SUNXI_CCU_MUX_TABLE(_shift, _width, _table), \
.common = { \
.reg = _reg, \
.hw.init = CLK_HW_INIT_PARENTS(_name, \
@@ -49,17 +60,14 @@ struct ccu_mux {
#define SUNXI_CCU_MUX_WITH_GATE(_struct, _name, _parents, _reg, \
_shift, _width, _gate, _flags) \
- struct ccu_mux _struct = { \
- .enable = _gate, \
- .mux = SUNXI_CLK_MUX(_shift, _width), \
- .common = { \
- .reg = _reg, \
- .hw.init = CLK_HW_INIT_PARENTS(_name, \
- _parents, \
- &ccu_mux_ops, \
- _flags), \
- } \
- }
+ SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, NULL, \
+ _reg, _shift, _width, _gate, \
+ _flags)
+
+#define SUNXI_CCU_MUX(_struct, _name, _parents, _reg, _shift, _width, \
+ _flags) \
+ SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, NULL, \
+ _reg, _shift, _width, 0, _flags)
static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw)
{
@@ -88,4 +96,18 @@ int ccu_mux_helper_set_parent(struct ccu_common *common,
struct ccu_mux_internal *cm,
u8 index);
+struct ccu_mux_nb {
+ struct notifier_block clk_nb;
+ struct ccu_common *common;
+ struct ccu_mux_internal *cm;
+
+ u32 delay_us; /* How many us to wait after reparenting */
+ u8 bypass_index; /* Which parent to temporarily use */
+ u8 original_index; /* This is set by the notifier callback */
+};
+
+#define to_ccu_mux_nb(_nb) container_of(_nb, struct ccu_mux_nb, clk_nb)
+
+int ccu_mux_notifier_register(struct clk *clk, struct ccu_mux_nb *mux_nb);
+
#endif /* _CCU_MUX_H_ */
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
index 2071822..059fdc3 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.c
+++ b/drivers/clk/sunxi-ng/ccu_nkm.c
@@ -93,19 +93,30 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw,
return parent_rate * (n + 1) * (k + 1) / (m + 1);
}
-static long ccu_nkm_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
+ unsigned long parent_rate,
+ unsigned long rate,
+ void *data)
{
- struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
+ struct ccu_nkm *nkm = data;
struct _ccu_nkm _nkm;
_nkm.max_n = 1 << nkm->n.width;
_nkm.max_k = 1 << nkm->k.width;
- _nkm.max_m = 1 << nkm->m.width;
+ _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
- ccu_nkm_find_best(*parent_rate, rate, &_nkm);
+ ccu_nkm_find_best(parent_rate, rate, &_nkm);
- return *parent_rate * _nkm.n * _nkm.k / _nkm.m;
+ return parent_rate * _nkm.n * _nkm.k / _nkm.m;
+}
+
+static int ccu_nkm_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
+
+ return ccu_mux_helper_determine_rate(&nkm->common, &nkm->mux,
+ req, ccu_nkm_round_rate, nkm);
}
static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -118,7 +129,7 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
_nkm.max_n = 1 << nkm->n.width;
_nkm.max_k = 1 << nkm->k.width;
- _nkm.max_m = 1 << nkm->m.width;
+ _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
ccu_nkm_find_best(parent_rate, rate, &_nkm);
@@ -142,12 +153,29 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
+static u8 ccu_nkm_get_parent(struct clk_hw *hw)
+{
+ struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
+
+ return ccu_mux_helper_get_parent(&nkm->common, &nkm->mux);
+}
+
+static int ccu_nkm_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
+
+ return ccu_mux_helper_set_parent(&nkm->common, &nkm->mux, index);
+}
+
const struct clk_ops ccu_nkm_ops = {
.disable = ccu_nkm_disable,
.enable = ccu_nkm_enable,
.is_enabled = ccu_nkm_is_enabled,
+ .get_parent = ccu_nkm_get_parent,
+ .set_parent = ccu_nkm_set_parent,
+
+ .determine_rate = ccu_nkm_determine_rate,
.recalc_rate = ccu_nkm_recalc_rate,
- .round_rate = ccu_nkm_round_rate,
.set_rate = ccu_nkm_set_rate,
};
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.h b/drivers/clk/sunxi-ng/ccu_nkm.h
index 1936ac1..35493fd 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.h
+++ b/drivers/clk/sunxi-ng/ccu_nkm.h
@@ -32,10 +32,33 @@ struct ccu_nkm {
struct _ccu_mult n;
struct _ccu_mult k;
struct _ccu_div m;
+ struct ccu_mux_internal mux;
struct ccu_common common;
};
+#define SUNXI_CCU_NKM_WITH_MUX_GATE_LOCK(_struct, _name, _parents, _reg, \
+ _nshift, _nwidth, \
+ _kshift, _kwidth, \
+ _mshift, _mwidth, \
+ _muxshift, _muxwidth, \
+ _gate, _lock, _flags) \
+ struct ccu_nkm _struct = { \
+ .enable = _gate, \
+ .lock = _lock, \
+ .k = _SUNXI_CCU_MULT(_kshift, _kwidth), \
+ .n = _SUNXI_CCU_MULT(_nshift, _nwidth), \
+ .m = _SUNXI_CCU_DIV(_mshift, _mwidth), \
+ .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \
+ .common = { \
+ .reg = _reg, \
+ .hw.init = CLK_HW_INIT_PARENTS(_name, \
+ _parents, \
+ &ccu_nkm_ops, \
+ _flags), \
+ }, \
+ }
+
#define SUNXI_CCU_NKM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \
_nshift, _nwidth, \
_kshift, _kwidth, \
diff --git a/drivers/clk/sunxi-ng/ccu_nkmp.c b/drivers/clk/sunxi-ng/ccu_nkmp.c
index 9f2b98e..9769dee 100644
--- a/drivers/clk/sunxi-ng/ccu_nkmp.c
+++ b/drivers/clk/sunxi-ng/ccu_nkmp.c
@@ -29,14 +29,14 @@ static void ccu_nkmp_find_best(unsigned long parent, unsigned long rate,
unsigned long _n, _k, _m, _p;
for (_k = 1; _k <= nkmp->max_k; _k++) {
- for (_p = 0; _p <= nkmp->max_p; _p++) {
+ for (_p = 1; _p <= nkmp->max_p; _p <<= 1) {
unsigned long tmp_rate;
- rational_best_approximation(rate / _k, parent >> _p,
+ rational_best_approximation(rate / _k, parent / _p,
nkmp->max_n, nkmp->max_m,
&_n, &_m);
- tmp_rate = (parent * _n * _k >> _p) / _m;
+ tmp_rate = parent * _n * _k / (_m * _p);
if (tmp_rate > rate)
continue;
@@ -110,13 +110,12 @@ static long ccu_nkmp_round_rate(struct clk_hw *hw, unsigned long rate,
_nkmp.max_n = 1 << nkmp->n.width;
_nkmp.max_k = 1 << nkmp->k.width;
- _nkmp.max_m = 1 << nkmp->m.width;
- _nkmp.max_p = (1 << nkmp->p.width) - 1;
+ _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
+ _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
- ccu_nkmp_find_best(*parent_rate, rate,
- &_nkmp);
+ ccu_nkmp_find_best(*parent_rate, rate, &_nkmp);
- return (*parent_rate * _nkmp.n * _nkmp.k >> _nkmp.p) / _nkmp.m;
+ return *parent_rate * _nkmp.n * _nkmp.k / (_nkmp.m * _nkmp.p);
}
static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -129,8 +128,8 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
_nkmp.max_n = 1 << nkmp->n.width;
_nkmp.max_k = 1 << nkmp->k.width;
- _nkmp.max_m = 1 << nkmp->m.width;
- _nkmp.max_p = (1 << nkmp->p.width) - 1;
+ _nkmp.max_m = nkmp->m.max ?: 1 << nkmp->m.width;
+ _nkmp.max_p = nkmp->p.max ?: 1 << ((1 << nkmp->p.width) - 1);
ccu_nkmp_find_best(parent_rate, rate, &_nkmp);
@@ -145,7 +144,7 @@ static int ccu_nkmp_set_rate(struct clk_hw *hw, unsigned long rate,
reg |= (_nkmp.n - 1) << nkmp->n.shift;
reg |= (_nkmp.k - 1) << nkmp->k.shift;
reg |= (_nkmp.m - 1) << nkmp->m.shift;
- reg |= _nkmp.p << nkmp->p.shift;
+ reg |= ilog2(_nkmp.p) << nkmp->p.shift;
writel(reg, nkmp->common.base + nkmp->common.reg);
diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c
index e35ddd8..b61bdd8 100644
--- a/drivers/clk/sunxi-ng/ccu_nm.c
+++ b/drivers/clk/sunxi-ng/ccu_nm.c
@@ -61,11 +61,13 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct ccu_nm *nm = hw_to_ccu_nm(hw);
+ unsigned long max_n, max_m;
unsigned long n, m;
- rational_best_approximation(rate, *parent_rate,
- 1 << nm->n.width, 1 << nm->m.width,
- &n, &m);
+ max_n = 1 << nm->n.width;
+ max_m = nm->m.max ?: 1 << nm->m.width;
+
+ rational_best_approximation(rate, *parent_rate, max_n, max_m, &n, &m);
return *parent_rate * n / m;
}
@@ -75,6 +77,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct ccu_nm *nm = hw_to_ccu_nm(hw);
unsigned long flags;
+ unsigned long max_n, max_m;
unsigned long n, m;
u32 reg;
@@ -83,9 +86,10 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate,
else
ccu_frac_helper_disable(&nm->common, &nm->frac);
- rational_best_approximation(rate, parent_rate,
- 1 << nm->n.width, 1 << nm->m.width,
- &n, &m);
+ max_n = 1 << nm->n.width;
+ max_m = nm->m.max ?: 1 << nm->m.width;
+
+ rational_best_approximation(rate, parent_rate, max_n, max_m, &n, &m);
spin_lock_irqsave(nm->common.lock, flags);
diff --git a/drivers/clk/sunxi/clk-mod0.c b/drivers/clk/sunxi/clk-mod0.c
index b38d71c..e54266c 100644
--- a/drivers/clk/sunxi/clk-mod0.c
+++ b/drivers/clk/sunxi/clk-mod0.c
@@ -91,7 +91,8 @@ static void __init sun4i_a10_mod0_setup(struct device_node *node)
sunxi_factors_register(node, &sun4i_a10_mod0_data,
&sun4i_a10_mod0_lock, reg);
}
-CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup);
+CLK_OF_DECLARE_DRIVER(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk",
+ sun4i_a10_mod0_setup);
static int sun4i_a10_mod0_clk_probe(struct platform_device *pdev)
{
diff --git a/drivers/clk/sunxi/clk-sun8i-apb0.c b/drivers/clk/sunxi/clk-sun8i-apb0.c
index a5666e1..ea1eed2 100644
--- a/drivers/clk/sunxi/clk-sun8i-apb0.c
+++ b/drivers/clk/sunxi/clk-sun8i-apb0.c
@@ -82,8 +82,8 @@ err_unmap:
of_address_to_resource(node, 0, &res);
release_mem_region(res.start, resource_size(&res));
}
-CLK_OF_DECLARE(sun8i_a23_apb0, "allwinner,sun8i-a23-apb0-clk",
- sun8i_a23_apb0_setup);
+CLK_OF_DECLARE_DRIVER(sun8i_a23_apb0, "allwinner,sun8i-a23-apb0-clk",
+ sun8i_a23_apb0_setup);
static int sun8i_a23_apb0_clk_probe(struct platform_device *pdev)
{
diff --git a/drivers/clk/uniphier/Kconfig b/drivers/clk/uniphier/Kconfig
new file mode 100644
index 0000000..5512377
--- /dev/null
+++ b/drivers/clk/uniphier/Kconfig
@@ -0,0 +1,9 @@
+config CLK_UNIPHIER
+ bool "Clock driver for UniPhier SoCs"
+ depends on ARCH_UNIPHIER || COMPILE_TEST
+ depends on OF && MFD_SYSCON
+ default ARCH_UNIPHIER
+ help
+ Support for clock controllers on UniPhier SoCs.
+ Say Y if you want to control clocks provided by System Control
+ block, Media I/O block, Peripheral Block.
diff --git a/drivers/clk/uniphier/Makefile b/drivers/clk/uniphier/Makefile
new file mode 100644
index 0000000..f27b3603
--- /dev/null
+++ b/drivers/clk/uniphier/Makefile
@@ -0,0 +1,8 @@
+obj-y += clk-uniphier-core.o
+obj-y += clk-uniphier-fixed-factor.o
+obj-y += clk-uniphier-fixed-rate.o
+obj-y += clk-uniphier-gate.o
+obj-y += clk-uniphier-mux.o
+obj-y += clk-uniphier-sys.o
+obj-y += clk-uniphier-mio.o
+obj-y += clk-uniphier-peri.o
diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c
new file mode 100644
index 0000000..5ffb898
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-core.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-uniphier.h"
+
+static struct clk_hw *uniphier_clk_register(struct device *dev,
+ struct regmap *regmap,
+ const struct uniphier_clk_data *data)
+{
+ switch (data->type) {
+ case UNIPHIER_CLK_TYPE_FIXED_FACTOR:
+ return uniphier_clk_register_fixed_factor(dev, data->name,
+ &data->data.factor);
+ case UNIPHIER_CLK_TYPE_FIXED_RATE:
+ return uniphier_clk_register_fixed_rate(dev, data->name,
+ &data->data.rate);
+ case UNIPHIER_CLK_TYPE_GATE:
+ return uniphier_clk_register_gate(dev, regmap, data->name,
+ &data->data.gate);
+ case UNIPHIER_CLK_TYPE_MUX:
+ return uniphier_clk_register_mux(dev, regmap, data->name,
+ &data->data.mux);
+ default:
+ dev_err(dev, "unsupported clock type\n");
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static int uniphier_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct clk_hw_onecell_data *hw_data;
+ const struct uniphier_clk_data *p, *data;
+ struct regmap *regmap;
+ struct device_node *parent;
+ int clk_num = 0;
+
+ data = of_device_get_match_data(dev);
+ if (WARN_ON(!data))
+ return -EINVAL;
+
+ parent = of_get_parent(dev->of_node); /* parent should be syscon node */
+ regmap = syscon_node_to_regmap(parent);
+ of_node_put(parent);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "failed to get regmap (error %ld)\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ for (p = data; p->name; p++)
+ clk_num = max(clk_num, p->idx + 1);
+
+ hw_data = devm_kzalloc(dev,
+ sizeof(*hw_data) + clk_num * sizeof(struct clk_hw *),
+ GFP_KERNEL);
+ if (!hw_data)
+ return -ENOMEM;
+
+ hw_data->num = clk_num;
+
+ /* avoid returning NULL for unused idx */
+ for (; clk_num >= 0; clk_num--)
+ hw_data->hws[clk_num] = ERR_PTR(-EINVAL);
+
+ for (p = data; p->name; p++) {
+ struct clk_hw *hw;
+
+ dev_dbg(dev, "register %s (index=%d)\n", p->name, p->idx);
+ hw = uniphier_clk_register(dev, regmap, p);
+ if (IS_ERR(hw)) {
+ dev_err(dev, "failed to register %s (error %ld)\n",
+ p->name, PTR_ERR(hw));
+ return PTR_ERR(hw);
+ }
+
+ if (p->idx >= 0)
+ hw_data->hws[p->idx] = hw;
+ }
+
+ return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+ hw_data);
+}
+
+static int uniphier_clk_remove(struct platform_device *pdev)
+{
+ of_clk_del_provider(pdev->dev.of_node);
+
+ return 0;
+}
+
+static const struct of_device_id uniphier_clk_match[] = {
+ /* System clock */
+ {
+ .compatible = "socionext,uniphier-ld4-clock",
+ .data = uniphier_ld4_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro4-clock",
+ .data = uniphier_pro4_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-sld8-clock",
+ .data = uniphier_sld8_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-clock",
+ .data = uniphier_pro5_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-clock",
+ .data = uniphier_pxs2_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-clock",
+ .data = uniphier_ld11_sys_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-clock",
+ .data = uniphier_ld20_sys_clk_data,
+ },
+ /* Media I/O clock */
+ {
+ .compatible = "socionext,uniphier-sld3-mio-clock",
+ .data = uniphier_sld3_mio_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld4-mio-clock",
+ .data = uniphier_sld3_mio_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro4-mio-clock",
+ .data = uniphier_sld3_mio_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-sld8-mio-clock",
+ .data = uniphier_sld3_mio_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-mio-clock",
+ .data = uniphier_pro5_mio_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-mio-clock",
+ .data = uniphier_pro5_mio_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-mio-clock",
+ .data = uniphier_sld3_mio_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-mio-clock",
+ .data = uniphier_pro5_mio_clk_data,
+ },
+ /* Peripheral clock */
+ {
+ .compatible = "socionext,uniphier-ld4-peri-clock",
+ .data = uniphier_ld4_peri_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro4-peri-clock",
+ .data = uniphier_pro4_peri_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-sld8-peri-clock",
+ .data = uniphier_ld4_peri_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pro5-peri-clock",
+ .data = uniphier_pro4_peri_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-pxs2-peri-clock",
+ .data = uniphier_pro4_peri_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld11-peri-clock",
+ .data = uniphier_pro4_peri_clk_data,
+ },
+ {
+ .compatible = "socionext,uniphier-ld20-peri-clock",
+ .data = uniphier_pro4_peri_clk_data,
+ },
+ { /* sentinel */ }
+};
+
+static struct platform_driver uniphier_clk_driver = {
+ .probe = uniphier_clk_probe,
+ .remove = uniphier_clk_remove,
+ .driver = {
+ .name = "uniphier-clk",
+ .of_match_table = uniphier_clk_match,
+ },
+};
+builtin_platform_driver(uniphier_clk_driver);
diff --git a/drivers/clk/uniphier/clk-uniphier-fixed-factor.c b/drivers/clk/uniphier/clk-uniphier-fixed-factor.c
new file mode 100644
index 0000000..da2d9f4
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-fixed-factor.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+
+#include "clk-uniphier.h"
+
+struct clk_hw *uniphier_clk_register_fixed_factor(struct device *dev,
+ const char *name,
+ const struct uniphier_clk_fixed_factor_data *data)
+{
+ struct clk_fixed_factor *fix;
+ struct clk_init_data init;
+ int ret;
+
+ fix = devm_kzalloc(dev, sizeof(*fix), GFP_KERNEL);
+ if (!fix)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_fixed_factor_ops;
+ init.flags = data->parent_name ? CLK_SET_RATE_PARENT : 0;
+ init.parent_names = data->parent_name ? &data->parent_name : NULL;
+ init.num_parents = data->parent_name ? 1 : 0;
+
+ fix->mult = data->mult;
+ fix->div = data->div;
+ fix->hw.init = &init;
+
+ ret = devm_clk_hw_register(dev, &fix->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &fix->hw;
+}
diff --git a/drivers/clk/uniphier/clk-uniphier-fixed-rate.c b/drivers/clk/uniphier/clk-uniphier-fixed-rate.c
new file mode 100644
index 0000000..0ad0d46
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-fixed-rate.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+
+#include "clk-uniphier.h"
+
+struct clk_hw *uniphier_clk_register_fixed_rate(struct device *dev,
+ const char *name,
+ const struct uniphier_clk_fixed_rate_data *data)
+{
+ struct clk_fixed_rate *fixed;
+ struct clk_init_data init;
+ int ret;
+
+ /* allocate fixed-rate clock */
+ fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
+ if (!fixed)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_fixed_rate_ops;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+
+ fixed->fixed_rate = data->fixed_rate;
+ fixed->hw.init = &init;
+
+ ret = devm_clk_hw_register(dev, &fixed->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &fixed->hw;
+}
diff --git a/drivers/clk/uniphier/clk-uniphier-gate.c b/drivers/clk/uniphier/clk-uniphier-gate.c
new file mode 100644
index 0000000..49142d4
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-gate.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+#include "clk-uniphier.h"
+
+struct uniphier_clk_gate {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ unsigned int reg;
+ unsigned int bit;
+};
+
+#define to_uniphier_clk_gate(_hw) \
+ container_of(_hw, struct uniphier_clk_gate, hw)
+
+static int uniphier_clk_gate_endisable(struct clk_hw *hw, int enable)
+{
+ struct uniphier_clk_gate *gate = to_uniphier_clk_gate(hw);
+
+ return regmap_write_bits(gate->regmap, gate->reg, BIT(gate->bit),
+ enable ? BIT(gate->bit) : 0);
+}
+
+static int uniphier_clk_gate_enable(struct clk_hw *hw)
+{
+ return uniphier_clk_gate_endisable(hw, 1);
+}
+
+static void uniphier_clk_gate_disable(struct clk_hw *hw)
+{
+ if (uniphier_clk_gate_endisable(hw, 0) < 0)
+ pr_warn("failed to disable clk\n");
+}
+
+static int uniphier_clk_gate_is_enabled(struct clk_hw *hw)
+{
+ struct uniphier_clk_gate *gate = to_uniphier_clk_gate(hw);
+ unsigned int val;
+
+ if (regmap_read(gate->regmap, gate->reg, &val) < 0)
+ pr_warn("is_enabled() may return wrong result\n");
+
+ return !!(val & BIT(gate->bit));
+}
+
+static const struct clk_ops uniphier_clk_gate_ops = {
+ .enable = uniphier_clk_gate_enable,
+ .disable = uniphier_clk_gate_disable,
+ .is_enabled = uniphier_clk_gate_is_enabled,
+};
+
+struct clk_hw *uniphier_clk_register_gate(struct device *dev,
+ struct regmap *regmap,
+ const char *name,
+ const struct uniphier_clk_gate_data *data)
+{
+ struct uniphier_clk_gate *gate;
+ struct clk_init_data init;
+ int ret;
+
+ gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &uniphier_clk_gate_ops;
+ init.flags = data->parent_name ? CLK_SET_RATE_PARENT : 0;
+ init.parent_names = data->parent_name ? &data->parent_name : NULL;
+ init.num_parents = data->parent_name ? 1 : 0;
+
+ gate->regmap = regmap;
+ gate->reg = data->reg;
+ gate->bit = data->bit;
+ gate->hw.init = &init;
+
+ ret = devm_clk_hw_register(dev, &gate->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &gate->hw;
+}
diff --git a/drivers/clk/uniphier/clk-uniphier-mio.c b/drivers/clk/uniphier/clk-uniphier-mio.c
new file mode 100644
index 0000000..6aa7ec7
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-mio.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "clk-uniphier.h"
+
+#define UNIPHIER_MIO_CLK_SD_FIXED \
+ UNIPHIER_CLK_FACTOR("sd-44m", -1, "sd-133m", 1, 3), \
+ UNIPHIER_CLK_FACTOR("sd-33m", -1, "sd-200m", 1, 6), \
+ UNIPHIER_CLK_FACTOR("sd-50m", -1, "sd-200m", 1, 4), \
+ UNIPHIER_CLK_FACTOR("sd-67m", -1, "sd-200m", 1, 3), \
+ UNIPHIER_CLK_FACTOR("sd-100m", -1, "sd-200m", 1, 2), \
+ UNIPHIER_CLK_FACTOR("sd-40m", -1, "sd-200m", 1, 5), \
+ UNIPHIER_CLK_FACTOR("sd-25m", -1, "sd-200m", 1, 8), \
+ UNIPHIER_CLK_FACTOR("sd-22m", -1, "sd-133m", 1, 6)
+
+#define UNIPHIER_MIO_CLK_SD(_idx, ch) \
+ { \
+ .name = "sd" #ch "-sel", \
+ .type = UNIPHIER_CLK_TYPE_MUX, \
+ .idx = -1, \
+ .data.mux = { \
+ .parent_names = { \
+ "sd-44m", \
+ "sd-33m", \
+ "sd-50m", \
+ "sd-67m", \
+ "sd-100m", \
+ "sd-40m", \
+ "sd-25m", \
+ "sd-22m", \
+ }, \
+ .num_parents = 8, \
+ .reg = 0x30 + 0x200 * (ch), \
+ .masks = { \
+ 0x00031000, \
+ 0x00031000, \
+ 0x00031000, \
+ 0x00031000, \
+ 0x00001300, \
+ 0x00001300, \
+ 0x00001300, \
+ 0x00001300, \
+ }, \
+ .vals = { \
+ 0x00000000, \
+ 0x00010000, \
+ 0x00020000, \
+ 0x00030000, \
+ 0x00001000, \
+ 0x00001100, \
+ 0x00001200, \
+ 0x00001300, \
+ }, \
+ }, \
+ }, \
+ UNIPHIER_CLK_GATE("sd" #ch, (_idx), "sd" #ch "-sel", 0x20 + 0x200 * (ch), 8)
+
+#define UNIPHIER_MIO_CLK_USB2(idx, ch) \
+ UNIPHIER_CLK_GATE("usb2" #ch, (idx), "usb2", 0x20 + 0x200 * (ch), 28)
+
+#define UNIPHIER_MIO_CLK_USB2_PHY(idx, ch) \
+ UNIPHIER_CLK_GATE("usb2" #ch "-phy", (idx), "usb2", 0x20 + 0x200 * (ch), 29)
+
+#define UNIPHIER_MIO_CLK_DMAC(idx) \
+ UNIPHIER_CLK_GATE("miodmac", (idx), "stdmac", 0x20, 25)
+
+const struct uniphier_clk_data uniphier_sld3_mio_clk_data[] = {
+ UNIPHIER_MIO_CLK_SD_FIXED,
+ UNIPHIER_MIO_CLK_SD(0, 0),
+ UNIPHIER_MIO_CLK_SD(1, 1),
+ UNIPHIER_MIO_CLK_SD(2, 2),
+ UNIPHIER_MIO_CLK_DMAC(7),
+ UNIPHIER_MIO_CLK_USB2(8, 0),
+ UNIPHIER_MIO_CLK_USB2(9, 1),
+ UNIPHIER_MIO_CLK_USB2(10, 2),
+ UNIPHIER_MIO_CLK_USB2(11, 3),
+ UNIPHIER_MIO_CLK_USB2_PHY(12, 0),
+ UNIPHIER_MIO_CLK_USB2_PHY(13, 1),
+ UNIPHIER_MIO_CLK_USB2_PHY(14, 2),
+ UNIPHIER_MIO_CLK_USB2_PHY(15, 3),
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_pro5_mio_clk_data[] = {
+ UNIPHIER_MIO_CLK_SD_FIXED,
+ UNIPHIER_MIO_CLK_SD(0, 0),
+ UNIPHIER_MIO_CLK_SD(1, 1),
+ { /* sentinel */ }
+};
diff --git a/drivers/clk/uniphier/clk-uniphier-mux.c b/drivers/clk/uniphier/clk-uniphier-mux.c
new file mode 100644
index 0000000..15a2f2c
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-mux.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+#include "clk-uniphier.h"
+
+struct uniphier_clk_mux {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ unsigned int reg;
+ const unsigned int *masks;
+ const unsigned int *vals;
+};
+
+#define to_uniphier_clk_mux(_hw) container_of(_hw, struct uniphier_clk_mux, hw)
+
+static int uniphier_clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct uniphier_clk_mux *mux = to_uniphier_clk_mux(hw);
+
+ return regmap_write_bits(mux->regmap, mux->reg, mux->masks[index],
+ mux->vals[index]);
+}
+
+static u8 uniphier_clk_mux_get_parent(struct clk_hw *hw)
+{
+ struct uniphier_clk_mux *mux = to_uniphier_clk_mux(hw);
+ int num_parents = clk_hw_get_num_parents(hw);
+ int ret;
+ u32 val;
+ u8 i;
+
+ ret = regmap_read(mux->regmap, mux->reg, &val);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_parents; i++)
+ if ((mux->masks[i] & val) == mux->vals[i])
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct clk_ops uniphier_clk_mux_ops = {
+ .determine_rate = __clk_mux_determine_rate,
+ .set_parent = uniphier_clk_mux_set_parent,
+ .get_parent = uniphier_clk_mux_get_parent,
+};
+
+struct clk_hw *uniphier_clk_register_mux(struct device *dev,
+ struct regmap *regmap,
+ const char *name,
+ const struct uniphier_clk_mux_data *data)
+{
+ struct uniphier_clk_mux *mux;
+ struct clk_init_data init;
+ int ret;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &uniphier_clk_mux_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = data->parent_names;
+ init.num_parents = data->num_parents,
+
+ mux->regmap = regmap;
+ mux->reg = data->reg;
+ mux->masks = data->masks;
+ mux->vals = data->vals;
+ mux->hw.init = &init;
+
+ ret = devm_clk_hw_register(dev, &mux->hw);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &mux->hw;
+}
diff --git a/drivers/clk/uniphier/clk-uniphier-peri.c b/drivers/clk/uniphier/clk-uniphier-peri.c
new file mode 100644
index 0000000..521c80e
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-peri.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "clk-uniphier.h"
+
+#define UNIPHIER_PERI_CLK_UART(idx, ch) \
+ UNIPHIER_CLK_GATE("uart" #ch, (idx), "uart", 0x24, 19 + (ch))
+
+#define UNIPHIER_PERI_CLK_I2C_COMMON \
+ UNIPHIER_CLK_GATE("i2c-common", -1, "i2c", 0x20, 1)
+
+#define UNIPHIER_PERI_CLK_I2C(idx, ch) \
+ UNIPHIER_CLK_GATE("i2c" #ch, (idx), "i2c-common", 0x24, 5 + (ch))
+
+#define UNIPHIER_PERI_CLK_FI2C(idx, ch) \
+ UNIPHIER_CLK_GATE("i2c" #ch, (idx), "i2c", 0x24, 24 + (ch))
+
+const struct uniphier_clk_data uniphier_ld4_peri_clk_data[] = {
+ UNIPHIER_PERI_CLK_UART(0, 0),
+ UNIPHIER_PERI_CLK_UART(1, 1),
+ UNIPHIER_PERI_CLK_UART(2, 2),
+ UNIPHIER_PERI_CLK_UART(3, 3),
+ UNIPHIER_PERI_CLK_I2C_COMMON,
+ UNIPHIER_PERI_CLK_I2C(4, 0),
+ UNIPHIER_PERI_CLK_I2C(5, 1),
+ UNIPHIER_PERI_CLK_I2C(6, 2),
+ UNIPHIER_PERI_CLK_I2C(7, 3),
+ UNIPHIER_PERI_CLK_I2C(8, 4),
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_pro4_peri_clk_data[] = {
+ UNIPHIER_PERI_CLK_UART(0, 0),
+ UNIPHIER_PERI_CLK_UART(1, 1),
+ UNIPHIER_PERI_CLK_UART(2, 2),
+ UNIPHIER_PERI_CLK_UART(3, 3),
+ UNIPHIER_PERI_CLK_FI2C(4, 0),
+ UNIPHIER_PERI_CLK_FI2C(5, 1),
+ UNIPHIER_PERI_CLK_FI2C(6, 2),
+ UNIPHIER_PERI_CLK_FI2C(7, 3),
+ UNIPHIER_PERI_CLK_FI2C(8, 4),
+ UNIPHIER_PERI_CLK_FI2C(9, 5),
+ UNIPHIER_PERI_CLK_FI2C(10, 6),
+ { /* sentinel */ }
+};
diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c
new file mode 100644
index 0000000..5d02999
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier-sys.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/stddef.h>
+
+#include "clk-uniphier.h"
+
+#define UNIPHIER_SLD3_SYS_CLK_SD \
+ UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 8), \
+ UNIPHIER_CLK_FACTOR("sd-133m", -1, "vpll27a", 1, 2)
+
+#define UNIPHIER_PRO5_SYS_CLK_SD \
+ UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 12), \
+ UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 18)
+
+#define UNIPHIER_LD20_SYS_CLK_SD \
+ UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 10), \
+ UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 15)
+
+#define UNIPHIER_SLD3_SYS_CLK_STDMAC(idx) \
+ UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x2104, 10)
+
+#define UNIPHIER_LD11_SYS_CLK_STDMAC(idx) \
+ UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x210c, 8)
+
+#define UNIPHIER_PRO4_SYS_CLK_GIO(idx) \
+ UNIPHIER_CLK_GATE("gio", (idx), NULL, 0x2104, 6)
+
+#define UNIPHIER_PRO4_SYS_CLK_USB3(idx, ch) \
+ UNIPHIER_CLK_GATE("usb3" #ch, (idx), NULL, 0x2104, 16 + (ch))
+
+const struct uniphier_clk_data uniphier_sld3_sys_clk_data[] = {
+ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 65, 1), /* 1597.44 MHz */
+ UNIPHIER_CLK_FACTOR("upll", -1, "ref", 6000, 512), /* 288 MHz */
+ UNIPHIER_CLK_FACTOR("a2pll", -1, "ref", 24, 1), /* 589.824 MHz */
+ UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */
+ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16),
+ UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16),
+ UNIPHIER_SLD3_SYS_CLK_SD,
+ UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
+ UNIPHIER_SLD3_SYS_CLK_STDMAC(8),
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = {
+ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 65, 1), /* 1597.44 MHz */
+ UNIPHIER_CLK_FACTOR("upll", -1, "ref", 6000, 512), /* 288 MHz */
+ UNIPHIER_CLK_FACTOR("a2pll", -1, "ref", 24, 1), /* 589.824 MHz */
+ UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */
+ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16),
+ UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16),
+ UNIPHIER_SLD3_SYS_CLK_SD,
+ UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
+ UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = {
+ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 64, 1), /* 1600 MHz */
+ UNIPHIER_CLK_FACTOR("upll", -1, "ref", 288, 25), /* 288 MHz */
+ UNIPHIER_CLK_FACTOR("a2pll", -1, "upll", 256, 125), /* 589.824 MHz */
+ UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */
+ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8),
+ UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32),
+ UNIPHIER_SLD3_SYS_CLK_SD,
+ UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
+ UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */
+ UNIPHIER_PRO4_SYS_CLK_GIO(12), /* Ether, SATA, USB3 */
+ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0),
+ UNIPHIER_PRO4_SYS_CLK_USB3(15, 1),
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_sld8_sys_clk_data[] = {
+ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 64, 1), /* 1600 MHz */
+ UNIPHIER_CLK_FACTOR("upll", -1, "ref", 288, 25), /* 288 MHz */
+ UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */
+ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 20),
+ UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16),
+ UNIPHIER_SLD3_SYS_CLK_SD,
+ UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
+ UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = {
+ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 120, 1), /* 2400 MHz */
+ UNIPHIER_CLK_FACTOR("dapll1", -1, "ref", 128, 1), /* 2560 MHz */
+ UNIPHIER_CLK_FACTOR("dapll2", -1, "ref", 144, 125), /* 2949.12 MHz */
+ UNIPHIER_CLK_FACTOR("uart", 0, "dapll2", 1, 40),
+ UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48),
+ UNIPHIER_PRO5_SYS_CLK_SD,
+ UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC */
+ UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */
+ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0),
+ UNIPHIER_PRO4_SYS_CLK_USB3(15, 1),
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = {
+ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 96, 1), /* 2400 MHz */
+ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 27),
+ UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48),
+ UNIPHIER_PRO5_SYS_CLK_SD,
+ UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, RLE */
+ /* GIO is always clock-enabled: no function for 0x2104 bit6 */
+ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0),
+ UNIPHIER_PRO4_SYS_CLK_USB3(15, 1),
+ /* The document mentions 0x2104 bit 18, but not functional */
+ UNIPHIER_CLK_GATE("usb30-phy", 16, NULL, 0x2104, 19),
+ UNIPHIER_CLK_GATE("usb31-phy", 20, NULL, 0x2104, 20),
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = {
+ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 80, 1), /* 2000 MHz */
+ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34),
+ UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40),
+ UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC, MIO */
+ UNIPHIER_CLK_FACTOR("usb2", -1, "ref", 24, 25),
+ { /* sentinel */ }
+};
+
+const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = {
+ UNIPHIER_CLK_FACTOR("spll", -1, "ref", 80, 1), /* 2000 MHz */
+ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34),
+ UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40),
+ UNIPHIER_LD20_SYS_CLK_SD,
+ UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC */
+ /* GIO is always clock-enabled: no function for 0x210c bit5 */
+ /*
+ * clock for USB Link is enabled by the logic "OR" of bit 14 and bit 15.
+ * We do not use bit 15 here.
+ */
+ UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14),
+ UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12),
+ UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13),
+ { /* sentinel */ }
+};
diff --git a/drivers/clk/uniphier/clk-uniphier.h b/drivers/clk/uniphier/clk-uniphier.h
new file mode 100644
index 0000000..3ae1840
--- /dev/null
+++ b/drivers/clk/uniphier/clk-uniphier.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CLK_UNIPHIER_H__
+#define __CLK_UNIPHIER_H__
+
+struct clk_hw;
+struct device;
+struct regmap;
+
+#define UNIPHIER_CLK_MUX_MAX_PARENTS 8
+
+enum uniphier_clk_type {
+ UNIPHIER_CLK_TYPE_FIXED_FACTOR,
+ UNIPHIER_CLK_TYPE_FIXED_RATE,
+ UNIPHIER_CLK_TYPE_GATE,
+ UNIPHIER_CLK_TYPE_MUX,
+};
+
+struct uniphier_clk_fixed_factor_data {
+ const char *parent_name;
+ unsigned int mult;
+ unsigned int div;
+};
+
+struct uniphier_clk_fixed_rate_data {
+ unsigned long fixed_rate;
+};
+
+struct uniphier_clk_gate_data {
+ const char *parent_name;
+ unsigned int reg;
+ unsigned int bit;
+};
+
+struct uniphier_clk_mux_data {
+ const char *parent_names[UNIPHIER_CLK_MUX_MAX_PARENTS];
+ unsigned int num_parents;
+ unsigned int reg;
+ unsigned int masks[UNIPHIER_CLK_MUX_MAX_PARENTS];
+ unsigned int vals[UNIPHIER_CLK_MUX_MAX_PARENTS];
+};
+
+struct uniphier_clk_data {
+ const char *name;
+ enum uniphier_clk_type type;
+ int idx;
+ union {
+ struct uniphier_clk_fixed_factor_data factor;
+ struct uniphier_clk_fixed_rate_data rate;
+ struct uniphier_clk_gate_data gate;
+ struct uniphier_clk_mux_data mux;
+ } data;
+};
+
+#define UNIPHIER_CLK_FACTOR(_name, _idx, _parent, _mult, _div) \
+ { \
+ .name = (_name), \
+ .type = UNIPHIER_CLK_TYPE_FIXED_FACTOR, \
+ .idx = (_idx), \
+ .data.factor = { \
+ .parent_name = (_parent), \
+ .mult = (_mult), \
+ .div = (_div), \
+ }, \
+ }
+
+
+#define UNIPHIER_CLK_GATE(_name, _idx, _parent, _reg, _bit) \
+ { \
+ .name = (_name), \
+ .type = UNIPHIER_CLK_TYPE_GATE, \
+ .idx = (_idx), \
+ .data.gate = { \
+ .parent_name = (_parent), \
+ .reg = (_reg), \
+ .bit = (_bit), \
+ }, \
+ }
+
+
+struct clk_hw *uniphier_clk_register_fixed_factor(struct device *dev,
+ const char *name,
+ const struct uniphier_clk_fixed_factor_data *data);
+struct clk_hw *uniphier_clk_register_fixed_rate(struct device *dev,
+ const char *name,
+ const struct uniphier_clk_fixed_rate_data *data);
+struct clk_hw *uniphier_clk_register_gate(struct device *dev,
+ struct regmap *regmap,
+ const char *name,
+ const struct uniphier_clk_gate_data *data);
+struct clk_hw *uniphier_clk_register_mux(struct device *dev,
+ struct regmap *regmap,
+ const char *name,
+ const struct uniphier_clk_mux_data *data);
+
+extern const struct uniphier_clk_data uniphier_sld3_sys_clk_data[];
+extern const struct uniphier_clk_data uniphier_ld4_sys_clk_data[];
+extern const struct uniphier_clk_data uniphier_pro4_sys_clk_data[];
+extern const struct uniphier_clk_data uniphier_sld8_sys_clk_data[];
+extern const struct uniphier_clk_data uniphier_pro5_sys_clk_data[];
+extern const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[];
+extern const struct uniphier_clk_data uniphier_ld11_sys_clk_data[];
+extern const struct uniphier_clk_data uniphier_ld20_sys_clk_data[];
+extern const struct uniphier_clk_data uniphier_sld3_mio_clk_data[];
+extern const struct uniphier_clk_data uniphier_pro5_mio_clk_data[];
+extern const struct uniphier_clk_data uniphier_ld4_peri_clk_data[];
+extern const struct uniphier_clk_data uniphier_pro4_peri_clk_data[];
+
+#endif /* __CLK_UNIPHIER_H__ */
diff --git a/drivers/clk/versatile/clk-icst.c b/drivers/clk/versatile/clk-icst.c
index 5e9b652..4faa944 100644
--- a/drivers/clk/versatile/clk-icst.c
+++ b/drivers/clk/versatile/clk-icst.c
@@ -27,6 +27,26 @@
/* Magic unlocking token used on all Versatile boards */
#define VERSATILE_LOCK_VAL 0xA05F
+#define VERSATILE_AUX_OSC_BITS 0x7FFFF
+#define INTEGRATOR_AP_CM_BITS 0xFF
+#define INTEGRATOR_AP_SYS_BITS 0xFF
+#define INTEGRATOR_CP_CM_CORE_BITS 0x7FF
+#define INTEGRATOR_CP_CM_MEM_BITS 0x7FF000
+
+#define INTEGRATOR_AP_PCI_25_33_MHZ BIT(8)
+
+/**
+ * enum icst_control_type - the type of ICST control register
+ */
+enum icst_control_type {
+ ICST_VERSATILE, /* The standard type, all control bits available */
+ ICST_INTEGRATOR_AP_CM, /* Only 8 bits of VDW available */
+ ICST_INTEGRATOR_AP_SYS, /* Only 8 bits of VDW available */
+ ICST_INTEGRATOR_AP_PCI, /* Odd bit pattern storage */
+ ICST_INTEGRATOR_CP_CM_CORE, /* Only 8 bits of VDW and 3 bits of OD */
+ ICST_INTEGRATOR_CP_CM_MEM, /* Only 8 bits of VDW and 3 bits of OD */
+};
+
/**
* struct clk_icst - ICST VCO clock wrapper
* @hw: corresponding clock hardware entry
@@ -34,6 +54,7 @@
* @lockreg: VCO lock register address
* @params: parameters for this ICST instance
* @rate: current rate
+ * @ctype: the type of control register for the ICST
*/
struct clk_icst {
struct clk_hw hw;
@@ -42,6 +63,7 @@ struct clk_icst {
u32 lockreg_off;
struct icst_params *params;
unsigned long rate;
+ enum icst_control_type ctype;
};
#define to_icst(_hw) container_of(_hw, struct clk_icst, hw)
@@ -59,6 +81,76 @@ static int vco_get(struct clk_icst *icst, struct icst_vco *vco)
ret = regmap_read(icst->map, icst->vcoreg_off, &val);
if (ret)
return ret;
+
+ /*
+ * The Integrator/AP core clock can only access the low eight
+ * bits of the v PLL divider. Bit 8 is tied low and always zero,
+ * r is hardwired to 22 and output divider s is hardwired to 1
+ * (divide by 2) according to the document
+ * "Integrator CM926EJ-S, CM946E-S, CM966E-S, CM1026EJ-S and
+ * CM1136JF-S User Guide" ARM DUI 0138E, page 3-13 thru 3-14.
+ */
+ if (icst->ctype == ICST_INTEGRATOR_AP_CM) {
+ vco->v = val & INTEGRATOR_AP_CM_BITS;
+ vco->r = 22;
+ vco->s = 1;
+ return 0;
+ }
+
+ /*
+ * The Integrator/AP system clock on the base board can only
+ * access the low eight bits of the v PLL divider. Bit 8 is tied low
+ * and always zero, r is hardwired to 46, and the output divider is
+ * hardwired to 3 (divide by 4) according to the document
+ * "Integrator AP ASIC Development Motherboard" ARM DUI 0098B,
+ * page 3-16.
+ */
+ if (icst->ctype == ICST_INTEGRATOR_AP_SYS) {
+ vco->v = val & INTEGRATOR_AP_SYS_BITS;
+ vco->r = 46;
+ vco->s = 3;
+ return 0;
+ }
+
+ /*
+ * The Integrator/AP PCI clock is using an odd pattern to create
+ * the child clock, basically a single bit called DIVX/Y is used
+ * to select between two different hardwired values: setting the
+ * bit to 0 yields v = 17, r = 22 and OD = 1, whereas setting the
+ * bit to 1 yields v = 14, r = 14 and OD = 1 giving the frequencies
+ * 33 or 25 MHz respectively.
+ */
+ if (icst->ctype == ICST_INTEGRATOR_AP_PCI) {
+ bool divxy = !!(val & INTEGRATOR_AP_PCI_25_33_MHZ);
+
+ vco->v = divxy ? 17 : 14;
+ vco->r = divxy ? 22 : 14;
+ vco->s = 1;
+ return 0;
+ }
+
+ /*
+ * The Integrator/CP core clock can access the low eight bits
+ * of the v PLL divider. Bit 8 is tied low and always zero,
+ * r is hardwired to 22 and the output divider s is accessible
+ * in bits 8 thru 10 according to the document
+ * "Integrator/CM940T, CM920T, CM740T, and CM720T User Guide"
+ * ARM DUI 0157A, page 3-20 thru 3-23 and 4-10.
+ */
+ if (icst->ctype == ICST_INTEGRATOR_CP_CM_CORE) {
+ vco->v = val & 0xFF;
+ vco->r = 22;
+ vco->s = (val >> 8) & 7;
+ return 0;
+ }
+
+ if (icst->ctype == ICST_INTEGRATOR_CP_CM_MEM) {
+ vco->v = (val >> 12) & 0xFF;
+ vco->r = 22;
+ vco->s = (val >> 20) & 7;
+ return 0;
+ }
+
vco->v = val & 0x1ff;
vco->r = (val >> 9) & 0x7f;
vco->s = (val >> 16) & 03;
@@ -72,22 +164,62 @@ static int vco_get(struct clk_icst *icst, struct icst_vco *vco)
*/
static int vco_set(struct clk_icst *icst, struct icst_vco vco)
{
+ u32 mask;
u32 val;
int ret;
- ret = regmap_read(icst->map, icst->vcoreg_off, &val);
- if (ret)
- return ret;
+ /* Mask the bits used by the VCO */
+ switch (icst->ctype) {
+ case ICST_INTEGRATOR_AP_CM:
+ mask = INTEGRATOR_AP_CM_BITS;
+ val = vco.v & 0xFF;
+ if (vco.v & 0x100)
+ pr_err("ICST error: tried to set bit 8 of VDW\n");
+ if (vco.s != 1)
+ pr_err("ICST error: tried to use VOD != 1\n");
+ if (vco.r != 22)
+ pr_err("ICST error: tried to use RDW != 22\n");
+ break;
+ case ICST_INTEGRATOR_AP_SYS:
+ mask = INTEGRATOR_AP_SYS_BITS;
+ val = vco.v & 0xFF;
+ if (vco.v & 0x100)
+ pr_err("ICST error: tried to set bit 8 of VDW\n");
+ if (vco.s != 3)
+ pr_err("ICST error: tried to use VOD != 1\n");
+ if (vco.r != 46)
+ pr_err("ICST error: tried to use RDW != 22\n");
+ break;
+ case ICST_INTEGRATOR_CP_CM_CORE:
+ mask = INTEGRATOR_CP_CM_CORE_BITS; /* Uses 12 bits */
+ val = (vco.v & 0xFF) | vco.s << 8;
+ if (vco.v & 0x100)
+ pr_err("ICST error: tried to set bit 8 of VDW\n");
+ if (vco.r != 22)
+ pr_err("ICST error: tried to use RDW != 22\n");
+ break;
+ case ICST_INTEGRATOR_CP_CM_MEM:
+ mask = INTEGRATOR_CP_CM_MEM_BITS; /* Uses 12 bits */
+ val = ((vco.v & 0xFF) << 12) | (vco.s << 20);
+ if (vco.v & 0x100)
+ pr_err("ICST error: tried to set bit 8 of VDW\n");
+ if (vco.r != 22)
+ pr_err("ICST error: tried to use RDW != 22\n");
+ break;
+ default:
+ /* Regular auxilary oscillator */
+ mask = VERSATILE_AUX_OSC_BITS;
+ val = vco.v | (vco.r << 9) | (vco.s << 16);
+ break;
+ }
- /* Mask the 18 bits used by the VCO */
- val &= ~0x7ffff;
- val |= vco.v | (vco.r << 9) | (vco.s << 16);
+ pr_debug("ICST: new val = 0x%08x\n", val);
/* This magic unlocks the VCO so it can be controlled */
ret = regmap_write(icst->map, icst->lockreg_off, VERSATILE_LOCK_VAL);
if (ret)
return ret;
- ret = regmap_write(icst->map, icst->vcoreg_off, val);
+ ret = regmap_update_bits(icst->map, icst->vcoreg_off, mask, val);
if (ret)
return ret;
/* This locks the VCO again */
@@ -121,6 +253,46 @@ static long icst_round_rate(struct clk_hw *hw, unsigned long rate,
struct clk_icst *icst = to_icst(hw);
struct icst_vco vco;
+ if (icst->ctype == ICST_INTEGRATOR_AP_CM ||
+ icst->ctype == ICST_INTEGRATOR_CP_CM_CORE) {
+ if (rate <= 12000000)
+ return 12000000;
+ if (rate >= 160000000)
+ return 160000000;
+ /* Slam to closest megahertz */
+ return DIV_ROUND_CLOSEST(rate, 1000000) * 1000000;
+ }
+
+ if (icst->ctype == ICST_INTEGRATOR_CP_CM_MEM) {
+ if (rate <= 6000000)
+ return 6000000;
+ if (rate >= 66000000)
+ return 66000000;
+ /* Slam to closest 0.5 megahertz */
+ return DIV_ROUND_CLOSEST(rate, 500000) * 500000;
+ }
+
+ if (icst->ctype == ICST_INTEGRATOR_AP_SYS) {
+ /* Divides between 3 and 50 MHz in steps of 0.25 MHz */
+ if (rate <= 3000000)
+ return 3000000;
+ if (rate >= 50000000)
+ return 5000000;
+ /* Slam to closest 0.25 MHz */
+ return DIV_ROUND_CLOSEST(rate, 250000) * 250000;
+ }
+
+ if (icst->ctype == ICST_INTEGRATOR_AP_PCI) {
+ /*
+ * If we're below or less than halfway from 25 to 33 MHz
+ * select 25 MHz
+ */
+ if (rate <= 25000000 || rate < 29000000)
+ return 25000000;
+ /* Else just return the default frequency */
+ return 33000000;
+ }
+
vco = icst_hz_to_vco(icst->params, rate);
return icst_hz(icst->params, vco);
}
@@ -131,6 +303,36 @@ static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
struct clk_icst *icst = to_icst(hw);
struct icst_vco vco;
+ if (icst->ctype == ICST_INTEGRATOR_AP_PCI) {
+ /* This clock is especially primitive */
+ unsigned int val;
+ int ret;
+
+ if (rate == 25000000) {
+ val = 0;
+ } else if (rate == 33000000) {
+ val = INTEGRATOR_AP_PCI_25_33_MHZ;
+ } else {
+ pr_err("ICST: cannot set PCI frequency %lu\n",
+ rate);
+ return -EINVAL;
+ }
+ ret = regmap_write(icst->map, icst->lockreg_off,
+ VERSATILE_LOCK_VAL);
+ if (ret)
+ return ret;
+ ret = regmap_update_bits(icst->map, icst->vcoreg_off,
+ INTEGRATOR_AP_PCI_25_33_MHZ,
+ val);
+ if (ret)
+ return ret;
+ /* This locks the VCO again */
+ ret = regmap_write(icst->map, icst->lockreg_off, 0);
+ if (ret)
+ return ret;
+ return 0;
+ }
+
if (parent_rate)
icst->params->ref = parent_rate;
vco = icst_hz_to_vco(icst->params, rate);
@@ -148,7 +350,8 @@ static struct clk *icst_clk_setup(struct device *dev,
const struct clk_icst_desc *desc,
const char *name,
const char *parent_name,
- struct regmap *map)
+ struct regmap *map,
+ enum icst_control_type ctype)
{
struct clk *clk;
struct clk_icst *icst;
@@ -178,6 +381,7 @@ static struct clk *icst_clk_setup(struct device *dev,
icst->params = pclone;
icst->vcoreg_off = desc->vco_offset;
icst->lockreg_off = desc->lock_offset;
+ icst->ctype = ctype;
clk = clk_register(dev, &icst->hw);
if (IS_ERR(clk)) {
@@ -206,7 +410,8 @@ struct clk *icst_clk_register(struct device *dev,
pr_err("could not initialize ICST regmap\n");
return ERR_CAST(map);
}
- return icst_clk_setup(dev, desc, name, parent_name, map);
+ return icst_clk_setup(dev, desc, name, parent_name, map,
+ ICST_VERSATILE);
}
EXPORT_SYMBOL_GPL(icst_clk_register);
@@ -239,6 +444,56 @@ static const struct icst_params icst307_params = {
.idx2s = icst307_idx2s,
};
+/**
+ * The core modules on the Integrator/AP and Integrator/CP have
+ * especially crippled ICST525 control.
+ */
+static const struct icst_params icst525_apcp_cm_params = {
+ .vco_max = ICST525_VCO_MAX_5V,
+ .vco_min = ICST525_VCO_MIN,
+ /* Minimum 12 MHz, VDW = 4 */
+ .vd_min = 12,
+ /*
+ * Maximum 160 MHz, VDW = 152 for all core modules, but
+ * CM926EJ-S, CM1026EJ-S and CM1136JF-S can actually
+ * go to 200 MHz (max VDW = 192).
+ */
+ .vd_max = 192,
+ /* r is hardcoded to 22 and this is the actual divisor, +2 */
+ .rd_min = 24,
+ .rd_max = 24,
+ .s2div = icst525_s2div,
+ .idx2s = icst525_idx2s,
+};
+
+static const struct icst_params icst525_ap_sys_params = {
+ .vco_max = ICST525_VCO_MAX_5V,
+ .vco_min = ICST525_VCO_MIN,
+ /* Minimum 3 MHz, VDW = 4 */
+ .vd_min = 3,
+ /* Maximum 50 MHz, VDW = 192 */
+ .vd_max = 50,
+ /* r is hardcoded to 46 and this is the actual divisor, +2 */
+ .rd_min = 48,
+ .rd_max = 48,
+ .s2div = icst525_s2div,
+ .idx2s = icst525_idx2s,
+};
+
+static const struct icst_params icst525_ap_pci_params = {
+ .vco_max = ICST525_VCO_MAX_5V,
+ .vco_min = ICST525_VCO_MIN,
+ /* Minimum 25 MHz */
+ .vd_min = 25,
+ /* Maximum 33 MHz */
+ .vd_max = 33,
+ /* r is hardcoded to 14 or 22 and this is the actual divisors +2 */
+ .rd_min = 16,
+ .rd_max = 24,
+ .s2div = icst525_s2div,
+ .idx2s = icst525_idx2s,
+};
+
static void __init of_syscon_icst_setup(struct device_node *np)
{
struct device_node *parent;
@@ -247,6 +502,7 @@ static void __init of_syscon_icst_setup(struct device_node *np)
const char *name = np->name;
const char *parent_name;
struct clk *regclk;
+ enum icst_control_type ctype;
/* We do not release this reference, we are using it perpetually */
parent = of_get_parent(np);
@@ -269,11 +525,28 @@ static void __init of_syscon_icst_setup(struct device_node *np)
return;
}
- if (of_device_is_compatible(np, "arm,syscon-icst525"))
+ if (of_device_is_compatible(np, "arm,syscon-icst525")) {
icst_desc.params = &icst525_params;
- else if (of_device_is_compatible(np, "arm,syscon-icst307"))
+ ctype = ICST_VERSATILE;
+ } else if (of_device_is_compatible(np, "arm,syscon-icst307")) {
icst_desc.params = &icst307_params;
- else {
+ ctype = ICST_VERSATILE;
+ } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-cm")) {
+ icst_desc.params = &icst525_apcp_cm_params;
+ ctype = ICST_INTEGRATOR_AP_CM;
+ } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-sys")) {
+ icst_desc.params = &icst525_ap_sys_params;
+ ctype = ICST_INTEGRATOR_AP_SYS;
+ } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorap-pci")) {
+ icst_desc.params = &icst525_ap_pci_params;
+ ctype = ICST_INTEGRATOR_AP_PCI;
+ } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorcp-cm-core")) {
+ icst_desc.params = &icst525_apcp_cm_params;
+ ctype = ICST_INTEGRATOR_CP_CM_CORE;
+ } else if (of_device_is_compatible(np, "arm,syscon-icst525-integratorcp-cm-mem")) {
+ icst_desc.params = &icst525_apcp_cm_params;
+ ctype = ICST_INTEGRATOR_CP_CM_MEM;
+ } else {
pr_err("unknown ICST clock %s\n", name);
return;
}
@@ -281,7 +554,7 @@ static void __init of_syscon_icst_setup(struct device_node *np)
/* Parent clock name is not the same as node parent */
parent_name = of_clk_get_parent_name(np, 0);
- regclk = icst_clk_setup(NULL, &icst_desc, name, parent_name, map);
+ regclk = icst_clk_setup(NULL, &icst_desc, name, parent_name, map, ctype);
if (IS_ERR(regclk)) {
pr_err("error setting up syscon ICST clock %s\n", name);
return;
@@ -294,5 +567,14 @@ CLK_OF_DECLARE(arm_syscon_icst525_clk,
"arm,syscon-icst525", of_syscon_icst_setup);
CLK_OF_DECLARE(arm_syscon_icst307_clk,
"arm,syscon-icst307", of_syscon_icst_setup);
-
+CLK_OF_DECLARE(arm_syscon_integratorap_cm_clk,
+ "arm,syscon-icst525-integratorap-cm", of_syscon_icst_setup);
+CLK_OF_DECLARE(arm_syscon_integratorap_sys_clk,
+ "arm,syscon-icst525-integratorap-sys", of_syscon_icst_setup);
+CLK_OF_DECLARE(arm_syscon_integratorap_pci_clk,
+ "arm,syscon-icst525-integratorap-pci", of_syscon_icst_setup);
+CLK_OF_DECLARE(arm_syscon_integratorcp_cm_core_clk,
+ "arm,syscon-icst525-integratorcp-cm-core", of_syscon_icst_setup);
+CLK_OF_DECLARE(arm_syscon_integratorcp_cm_mem_clk,
+ "arm,syscon-icst525-integratorcp-cm-mem", of_syscon_icst_setup);
#endif
diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile
index 74005aa..83374bf 100644
--- a/drivers/clk/zte/Makefile
+++ b/drivers/clk/zte/Makefile
@@ -1,2 +1,3 @@
obj-y := clk.o
obj-$(CONFIG_SOC_ZX296702) += clk-zx296702.o
+obj-$(CONFIG_ARCH_ZX) += clk-zx296718.o
diff --git a/drivers/clk/zte/clk-zx296718.c b/drivers/clk/zte/clk-zx296718.c
new file mode 100644
index 0000000..707d629
--- /dev/null
+++ b/drivers/clk/zte/clk-zx296718.c
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2015 - 2016 ZTE Corporation.
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/clock/zx296718-clock.h>
+#include "clk.h"
+
+/* TOP CRM */
+#define TOP_CLK_MUX0 0x04
+#define TOP_CLK_MUX1 0x08
+#define TOP_CLK_MUX2 0x0c
+#define TOP_CLK_MUX3 0x10
+#define TOP_CLK_MUX4 0x14
+#define TOP_CLK_MUX5 0x18
+#define TOP_CLK_MUX6 0x1c
+#define TOP_CLK_MUX7 0x20
+#define TOP_CLK_MUX9 0x28
+
+
+#define TOP_CLK_GATE0 0x34
+#define TOP_CLK_GATE1 0x38
+#define TOP_CLK_GATE2 0x3c
+#define TOP_CLK_GATE3 0x40
+#define TOP_CLK_GATE4 0x44
+#define TOP_CLK_GATE5 0x48
+#define TOP_CLK_GATE6 0x4c
+
+#define TOP_CLK_DIV0 0x58
+
+#define PLL_CPU_REG 0x80
+#define PLL_VGA_REG 0xb0
+#define PLL_DDR_REG 0xa0
+
+/* LSP0 CRM */
+#define LSP0_TIMER3_CLK 0x4
+#define LSP0_TIMER4_CLK 0x8
+#define LSP0_TIMER5_CLK 0xc
+#define LSP0_UART3_CLK 0x10
+#define LSP0_UART1_CLK 0x14
+#define LSP0_UART2_CLK 0x18
+#define LSP0_SPIFC0_CLK 0x1c
+#define LSP0_I2C4_CLK 0x20
+#define LSP0_I2C5_CLK 0x24
+#define LSP0_SSP0_CLK 0x28
+#define LSP0_SSP1_CLK 0x2c
+#define LSP0_USIM0_CLK 0x30
+#define LSP0_GPIO_CLK 0x34
+#define LSP0_I2C3_CLK 0x38
+
+/* LSP1 CRM */
+#define LSP1_UART4_CLK 0x08
+#define LSP1_UART5_CLK 0x0c
+#define LSP1_PWM_CLK 0x10
+#define LSP1_I2C2_CLK 0x14
+#define LSP1_SSP2_CLK 0x1c
+#define LSP1_SSP3_CLK 0x20
+#define LSP1_SSP4_CLK 0x24
+#define LSP1_USIM1_CLK 0x28
+
+/* audio lsp */
+#define AUDIO_I2S0_DIV_CFG1 0x10
+#define AUDIO_I2S0_DIV_CFG2 0x14
+#define AUDIO_I2S0_CLK 0x18
+#define AUDIO_I2S1_DIV_CFG1 0x20
+#define AUDIO_I2S1_DIV_CFG2 0x24
+#define AUDIO_I2S1_CLK 0x28
+#define AUDIO_I2S2_DIV_CFG1 0x30
+#define AUDIO_I2S2_DIV_CFG2 0x34
+#define AUDIO_I2S2_CLK 0x38
+#define AUDIO_I2S3_DIV_CFG1 0x40
+#define AUDIO_I2S3_DIV_CFG2 0x44
+#define AUDIO_I2S3_CLK 0x48
+#define AUDIO_I2C0_CLK 0x50
+#define AUDIO_SPDIF0_DIV_CFG1 0x60
+#define AUDIO_SPDIF0_DIV_CFG2 0x64
+#define AUDIO_SPDIF0_CLK 0x68
+#define AUDIO_SPDIF1_DIV_CFG1 0x70
+#define AUDIO_SPDIF1_DIV_CFG2 0x74
+#define AUDIO_SPDIF1_CLK 0x78
+#define AUDIO_TIMER_CLK 0x80
+#define AUDIO_TDM_CLK 0x90
+#define AUDIO_TS_CLK 0xa0
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static struct zx_pll_config pll_cpu_table[] = {
+ PLL_RATE(1312000000, 0x00103621, 0x04aaaaaa),
+ PLL_RATE(1407000000, 0x00103a21, 0x04aaaaaa),
+ PLL_RATE(1503000000, 0x00103e21, 0x04aaaaaa),
+ PLL_RATE(1600000000, 0x00104221, 0x04aaaaaa),
+};
+
+PNAME(osc) = {
+ "osc24m",
+ "osc32k",
+};
+
+PNAME(dbg_wclk_p) = {
+ "clk334m",
+ "clk466m",
+ "clk396m",
+ "clk250m",
+};
+
+PNAME(a72_coreclk_p) = {
+ "osc24m",
+ "pll_mm0_1188m",
+ "pll_mm1_1296m",
+ "clk1000m",
+ "clk648m",
+ "clk1600m",
+ "pll_audio_1800m",
+ "pll_vga_1800m",
+};
+
+PNAME(cpu_periclk_p) = {
+ "osc24m",
+ "clk500m",
+ "clk594m",
+ "clk466m",
+ "clk294m",
+ "clk334m",
+ "clk250m",
+ "clk125m",
+};
+
+PNAME(a53_coreclk_p) = {
+ "osc24m",
+ "clk1000m",
+ "pll_mm0_1188m",
+ "clk648m",
+ "clk500m",
+ "clk800m",
+ "clk1600m",
+ "pll_audio_1800m",
+};
+
+PNAME(sec_wclk_p) = {
+ "osc24m",
+ "clk396m",
+ "clk334m",
+ "clk297m",
+ "clk250m",
+ "clk198m",
+ "clk148m5",
+ "clk99m",
+};
+
+PNAME(sd_nand_wclk_p) = {
+ "osc24m",
+ "clk49m5",
+ "clk99m",
+ "clk198m",
+ "clk167m",
+ "clk148m5",
+ "clk125m",
+ "clk216m",
+};
+
+PNAME(emmc_wclk_p) = {
+ "osc24m",
+ "clk198m",
+ "clk99m",
+ "clk396m",
+ "clk334m",
+ "clk297m",
+ "clk250m",
+ "clk148m5",
+};
+
+PNAME(clk32_p) = {
+ "osc32k",
+ "clk32k768",
+};
+
+PNAME(usb_ref24m_p) = {
+ "osc32k",
+ "clk32k768",
+};
+
+PNAME(sys_noc_alck_p) = {
+ "osc24m",
+ "clk250m",
+ "clk198m",
+ "clk148m5",
+ "clk108m",
+ "clk54m",
+ "clk216m",
+ "clk240m",
+};
+
+PNAME(vde_aclk_p) = {
+ "clk334m",
+ "clk594m",
+ "clk500m",
+ "clk432m",
+ "clk480m",
+ "clk297m",
+ "clk_vga", /*600MHz*/
+ "clk294m",
+};
+
+PNAME(vce_aclk_p) = {
+ "clk334m",
+ "clk594m",
+ "clk500m",
+ "clk432m",
+ "clk396m",
+ "clk297m",
+ "clk_vga", /*600MHz*/
+ "clk294m",
+};
+
+PNAME(hde_aclk_p) = {
+ "clk334m",
+ "clk594m",
+ "clk500m",
+ "clk432m",
+ "clk396m",
+ "clk297m",
+ "clk_vga", /*600MHz*/
+ "clk294m",
+};
+
+PNAME(gpu_aclk_p) = {
+ "clk334m",
+ "clk648m",
+ "clk594m",
+ "clk500m",
+ "clk396m",
+ "clk297m",
+ "clk_vga", /*600MHz*/
+ "clk294m",
+};
+
+PNAME(sappu_aclk_p) = {
+ "clk396m",
+ "clk500m",
+ "clk250m",
+ "clk148m5",
+};
+
+PNAME(sappu_wclk_p) = {
+ "clk198m",
+ "clk396m",
+ "clk334m",
+ "clk297m",
+ "clk250m",
+ "clk148m5",
+ "clk125m",
+ "clk99m",
+};
+
+PNAME(vou_aclk_p) = {
+ "clk334m",
+ "clk594m",
+ "clk500m",
+ "clk432m",
+ "clk396m",
+ "clk297m",
+ "clk_vga", /*600MHz*/
+ "clk294m",
+};
+
+PNAME(vou_main_wclk_p) = {
+ "clk108m",
+ "clk594m",
+ "clk297m",
+ "clk148m5",
+ "clk74m25",
+ "clk54m",
+ "clk27m",
+ "clk_vga",
+};
+
+PNAME(vou_aux_wclk_p) = {
+ "clk108m",
+ "clk148m5",
+ "clk74m25",
+ "clk54m",
+ "clk27m",
+ "clk_vga",
+ "clk54m_mm0",
+ "clk"
+};
+
+PNAME(vou_ppu_wclk_p) = {
+ "clk334m",
+ "clk432m",
+ "clk396m",
+ "clk297m",
+ "clk250m",
+ "clk125m",
+ "clk198m",
+ "clk99m",
+};
+
+PNAME(vga_i2c_wclk_p) = {
+ "osc24m",
+ "clk99m",
+};
+
+PNAME(viu_m0_aclk_p) = {
+ "clk334m",
+ "clk432m",
+ "clk396m",
+ "clk297m",
+ "clk250m",
+ "clk125m",
+ "clk198m",
+ "osc24m",
+};
+
+PNAME(viu_m1_aclk_p) = {
+ "clk198m",
+ "clk250m",
+ "clk297m",
+ "clk125m",
+ "clk396m",
+ "clk334m",
+ "clk148m5",
+ "osc24m",
+};
+
+PNAME(viu_clk_p) = {
+ "clk198m",
+ "clk334m",
+ "clk297m",
+ "clk250m",
+ "clk396m",
+ "clk125m",
+ "clk99m",
+ "clk148m5",
+};
+
+PNAME(viu_jpeg_clk_p) = {
+ "clk334m",
+ "clk480m",
+ "clk432m",
+ "clk396m",
+ "clk297m",
+ "clk250m",
+ "clk125m",
+ "clk198m",
+};
+
+PNAME(ts_sys_clk_p) = {
+ "clk192m",
+ "clk167m",
+ "clk125m",
+ "clk99m",
+};
+
+PNAME(wdt_ares_p) = {
+ "osc24m",
+ "clk32k"
+};
+
+static struct clk_zx_pll zx296718_pll_clk[] = {
+ ZX296718_PLL("pll_cpu", "osc24m", PLL_CPU_REG, pll_cpu_table),
+};
+
+static struct zx_clk_fixed_factor top_ffactor_clk[] = {
+ FFACTOR(0, "clk4m", "osc24m", 1, 6, 0),
+ FFACTOR(0, "clk2m", "osc24m", 1, 12, 0),
+ /* pll cpu */
+ FFACTOR(0, "clk1600m", "pll_cpu", 1, 1, CLK_SET_RATE_PARENT),
+ FFACTOR(0, "clk800m", "pll_cpu", 1, 2, CLK_SET_RATE_PARENT),
+ /* pll mac */
+ FFACTOR(0, "clk25m", "pll_mac", 1, 40, 0),
+ FFACTOR(0, "clk125m", "pll_mac", 1, 8, 0),
+ FFACTOR(0, "clk250m", "pll_mac", 1, 4, 0),
+ FFACTOR(0, "clk50m", "pll_mac", 1, 20, 0),
+ FFACTOR(0, "clk500m", "pll_mac", 1, 2, 0),
+ FFACTOR(0, "clk1000m", "pll_mac", 1, 1, 0),
+ FFACTOR(0, "clk334m", "pll_mac", 1, 3, 0),
+ FFACTOR(0, "clk167m", "pll_mac", 1, 6, 0),
+ /* pll mm */
+ FFACTOR(0, "clk54m_mm0", "pll_mm0", 1, 22, 0),
+ FFACTOR(0, "clk74m25", "pll_mm0", 1, 16, 0),
+ FFACTOR(0, "clk148m5", "pll_mm0", 1, 8, 0),
+ FFACTOR(0, "clk297m", "pll_mm0", 1, 4, 0),
+ FFACTOR(0, "clk594m", "pll_mm0", 1, 2, 0),
+ FFACTOR(0, "pll_mm0_1188m", "pll_mm0", 1, 1, 0),
+ FFACTOR(0, "clk396m", "pll_mm0", 1, 3, 0),
+ FFACTOR(0, "clk198m", "pll_mm0", 1, 6, 0),
+ FFACTOR(0, "clk99m", "pll_mm0", 1, 12, 0),
+ FFACTOR(0, "clk49m5", "pll_mm0", 1, 24, 0),
+ /* pll mm */
+ FFACTOR(0, "clk324m", "pll_mm1", 1, 4, 0),
+ FFACTOR(0, "clk648m", "pll_mm1", 1, 2, 0),
+ FFACTOR(0, "pll_mm1_1296m", "pll_mm1", 1, 1, 0),
+ FFACTOR(0, "clk216m", "pll_mm1", 1, 6, 0),
+ FFACTOR(0, "clk432m", "pll_mm1", 1, 3, 0),
+ FFACTOR(0, "clk108m", "pll_mm1", 1, 12, 0),
+ FFACTOR(0, "clk72m", "pll_mm1", 1, 18, 0),
+ FFACTOR(0, "clk27m", "pll_mm1", 1, 48, 0),
+ FFACTOR(0, "clk54m", "pll_mm1", 1, 24, 0),
+ /* vga */
+ FFACTOR(0, "pll_vga_1800m", "pll_vga", 1, 1, 0),
+ FFACTOR(0, "clk_vga", "pll_vga", 1, 2, 0),
+ /* pll ddr */
+ FFACTOR(0, "clk466m", "pll_ddr", 1, 2, 0),
+
+ /* pll audio */
+ FFACTOR(0, "pll_audio_1800m", "pll_audio", 1, 1, 0),
+ FFACTOR(0, "clk32k768", "pll_audio", 1, 27000, 0),
+ FFACTOR(0, "clk16m384", "pll_audio", 1, 54, 0),
+ FFACTOR(0, "clk294m", "pll_audio", 1, 3, 0),
+
+ /* pll hsic*/
+ FFACTOR(0, "clk240m", "pll_hsic", 1, 4, 0),
+ FFACTOR(0, "clk480m", "pll_hsic", 1, 2, 0),
+ FFACTOR(0, "clk192m", "pll_hsic", 1, 5, 0),
+ FFACTOR(0, "clk_pll_24m", "pll_hsic", 1, 40, 0),
+ FFACTOR(0, "emmc_mux_div2", "emmc_mux", 1, 2, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_div_table noc_div_table[] = {
+ { .val = 1, .div = 2, },
+ { .val = 3, .div = 4, },
+};
+static struct zx_clk_div top_div_clk[] = {
+ DIV_T(0, "sys_noc_hclk", "sys_noc_aclk", TOP_CLK_DIV0, 0, 2, 0, noc_div_table),
+ DIV_T(0, "sys_noc_pclk", "sys_noc_aclk", TOP_CLK_DIV0, 4, 2, 0, noc_div_table),
+};
+
+static struct zx_clk_mux top_mux_clk[] = {
+ MUX(0, "dbg_mux", dbg_wclk_p, TOP_CLK_MUX0, 12, 2),
+ MUX(0, "a72_mux", a72_coreclk_p, TOP_CLK_MUX0, 8, 3),
+ MUX(0, "cpu_peri_mux", cpu_periclk_p, TOP_CLK_MUX0, 4, 3),
+ MUX_F(0, "a53_mux", a53_coreclk_p, TOP_CLK_MUX0, 0, 3, CLK_SET_RATE_PARENT, 0),
+ MUX(0, "sys_noc_aclk", sys_noc_alck_p, TOP_CLK_MUX1, 0, 3),
+ MUX(0, "sec_mux", sec_wclk_p, TOP_CLK_MUX2, 16, 3),
+ MUX(0, "sd1_mux", sd_nand_wclk_p, TOP_CLK_MUX2, 12, 3),
+ MUX(0, "sd0_mux", sd_nand_wclk_p, TOP_CLK_MUX2, 8, 3),
+ MUX(0, "emmc_mux", emmc_wclk_p, TOP_CLK_MUX2, 4, 3),
+ MUX(0, "nand_mux", sd_nand_wclk_p, TOP_CLK_MUX2, 0, 3),
+ MUX(0, "usb_ref24m_mux", usb_ref24m_p, TOP_CLK_MUX9, 16, 1),
+ MUX(0, "clk32k", clk32_p, TOP_CLK_MUX9, 12, 1),
+ MUX_F(0, "wdt_mux", wdt_ares_p, TOP_CLK_MUX9, 8, 1, CLK_SET_RATE_PARENT, 0),
+ MUX(0, "timer_mux", osc, TOP_CLK_MUX9, 4, 1),
+ MUX(0, "vde_mux", vde_aclk_p, TOP_CLK_MUX4, 0, 3),
+ MUX(0, "vce_mux", vce_aclk_p, TOP_CLK_MUX4, 4, 3),
+ MUX(0, "hde_mux", hde_aclk_p, TOP_CLK_MUX4, 8, 3),
+ MUX(0, "gpu_mux", gpu_aclk_p, TOP_CLK_MUX5, 0, 3),
+ MUX(0, "sappu_a_mux", sappu_aclk_p, TOP_CLK_MUX5, 4, 2),
+ MUX(0, "sappu_w_mux", sappu_wclk_p, TOP_CLK_MUX5, 8, 3),
+ MUX(0, "vou_a_mux", vou_aclk_p, TOP_CLK_MUX7, 0, 3),
+ MUX(0, "vou_main_w_mux", vou_main_wclk_p, TOP_CLK_MUX7, 4, 3),
+ MUX(0, "vou_aux_w_mux", vou_aux_wclk_p, TOP_CLK_MUX7, 8, 3),
+ MUX(0, "vou_ppu_w_mux", vou_ppu_wclk_p, TOP_CLK_MUX7, 12, 3),
+ MUX(0, "vga_i2c_mux", vga_i2c_wclk_p, TOP_CLK_MUX7, 16, 1),
+ MUX(0, "viu_m0_a_mux", viu_m0_aclk_p, TOP_CLK_MUX6, 0, 3),
+ MUX(0, "viu_m1_a_mux", viu_m1_aclk_p, TOP_CLK_MUX6, 4, 3),
+ MUX(0, "viu_w_mux", viu_clk_p, TOP_CLK_MUX6, 8, 3),
+ MUX(0, "viu_jpeg_w_mux", viu_jpeg_clk_p, TOP_CLK_MUX6, 12, 3),
+ MUX(0, "ts_sys_mux", ts_sys_clk_p, TOP_CLK_MUX6, 16, 2),
+};
+
+static struct zx_clk_gate top_gate_clk[] = {
+ GATE(CPU_DBG_GATE, "dbg_wclk", "dbg_mux", TOP_CLK_GATE0, 4, CLK_SET_RATE_PARENT, 0),
+ GATE(A72_GATE, "a72_coreclk", "a72_mux", TOP_CLK_GATE0, 3, CLK_SET_RATE_PARENT, 0),
+ GATE(CPU_PERI_GATE, "cpu_peri", "cpu_peri_mux", TOP_CLK_GATE0, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(A53_GATE, "a53_coreclk", "a53_mux", TOP_CLK_GATE0, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(SD1_WCLK, "sd1_wclk", "sd1_mux", TOP_CLK_GATE1, 13, CLK_SET_RATE_PARENT, 0),
+ GATE(SD0_WCLK, "sd0_wclk", "sd0_mux", TOP_CLK_GATE1, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(EMMC_WCLK, "emmc_wclk", "emmc_mux_div2", TOP_CLK_GATE0, 5, CLK_SET_RATE_PARENT, 0),
+ GATE(EMMC_NAND_AXI, "emmc_nand_aclk", "sys_noc_aclk", TOP_CLK_GATE1, 4, CLK_SET_RATE_PARENT, 0),
+ GATE(NAND_WCLK, "nand_wclk", "nand_mux", TOP_CLK_GATE0, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(EMMC_NAND_AHB, "emmc_nand_hclk", "sys_noc_hclk", TOP_CLK_GATE1, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(0, "lsp1_pclk", "sys_noc_pclk", TOP_CLK_GATE2, 31, 0, 0),
+ GATE(LSP1_148M5, "lsp1_148m5", "clk148m5", TOP_CLK_GATE2, 30, 0, 0),
+ GATE(LSP1_99M, "lsp1_99m", "clk99m", TOP_CLK_GATE2, 29, 0, 0),
+ GATE(LSP1_24M, "lsp1_24m", "osc24m", TOP_CLK_GATE2, 28, 0, 0),
+ GATE(LSP0_74M25, "lsp0_74m25", "clk74m25", TOP_CLK_GATE2, 25, 0, 0),
+ GATE(0, "lsp0_pclk", "sys_noc_pclk", TOP_CLK_GATE2, 24, 0, 0),
+ GATE(LSP0_32K, "lsp0_32k", "osc32k", TOP_CLK_GATE2, 23, 0, 0),
+ GATE(LSP0_148M5, "lsp0_148m5", "clk148m5", TOP_CLK_GATE2, 22, 0, 0),
+ GATE(LSP0_99M, "lsp0_99m", "clk99m", TOP_CLK_GATE2, 21, 0, 0),
+ GATE(LSP0_24M, "lsp0_24m", "osc24m", TOP_CLK_GATE2, 20, 0, 0),
+ GATE(AUDIO_99M, "audio_99m", "clk99m", TOP_CLK_GATE5, 27, 0, 0),
+ GATE(AUDIO_24M, "audio_24m", "osc24m", TOP_CLK_GATE5, 28, 0, 0),
+ GATE(AUDIO_16M384, "audio_16m384", "clk16m384", TOP_CLK_GATE5, 29, 0, 0),
+ GATE(AUDIO_32K, "audio_32k", "clk32k", TOP_CLK_GATE5, 30, 0, 0),
+ GATE(WDT_WCLK, "wdt_wclk", "wdt_mux", TOP_CLK_GATE6, 9, CLK_SET_RATE_PARENT, 0),
+ GATE(TIMER_WCLK, "timer_wclk", "timer_mux", TOP_CLK_GATE6, 5, CLK_SET_RATE_PARENT, 0),
+ GATE(VDE_ACLK, "vde_aclk", "vde_mux", TOP_CLK_GATE3, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(VCE_ACLK, "vce_aclk", "vce_mux", TOP_CLK_GATE3, 4, CLK_SET_RATE_PARENT, 0),
+ GATE(HDE_ACLK, "hde_aclk", "hde_mux", TOP_CLK_GATE3, 8, CLK_SET_RATE_PARENT, 0),
+ GATE(GPU_ACLK, "gpu_aclk", "gpu_mux", TOP_CLK_GATE3, 16, CLK_SET_RATE_PARENT, 0),
+ GATE(SAPPU_ACLK, "sappu_aclk", "sappu_a_mux", TOP_CLK_GATE3, 20, CLK_SET_RATE_PARENT, 0),
+ GATE(SAPPU_WCLK, "sappu_wclk", "sappu_w_mux", TOP_CLK_GATE3, 22, CLK_SET_RATE_PARENT, 0),
+ GATE(VOU_ACLK, "vou_aclk", "vou_a_mux", TOP_CLK_GATE4, 16, CLK_SET_RATE_PARENT, 0),
+ GATE(VOU_MAIN_WCLK, "vou_main_wclk", "vou_main_w_mux", TOP_CLK_GATE4, 18, CLK_SET_RATE_PARENT, 0),
+ GATE(VOU_AUX_WCLK, "vou_aux_wclk", "vou_aux_w_mux", TOP_CLK_GATE4, 19, CLK_SET_RATE_PARENT, 0),
+ GATE(VOU_PPU_WCLK, "vou_ppu_wclk", "vou_ppu_w_mux", TOP_CLK_GATE4, 20, CLK_SET_RATE_PARENT, 0),
+ GATE(MIPI_CFG_CLK, "mipi_cfg_clk", "osc24m", TOP_CLK_GATE4, 21, 0, 0),
+ GATE(VGA_I2C_WCLK, "vga_i2c_wclk", "vga_i2c_mux", TOP_CLK_GATE4, 23, CLK_SET_RATE_PARENT, 0),
+ GATE(MIPI_REF_CLK, "mipi_ref_clk", "clk27m", TOP_CLK_GATE4, 24, 0, 0),
+ GATE(HDMI_OSC_CEC, "hdmi_osc_cec", "clk2m", TOP_CLK_GATE4, 22, 0, 0),
+ GATE(HDMI_OSC_CLK, "hdmi_osc_clk", "clk240m", TOP_CLK_GATE4, 25, 0, 0),
+ GATE(HDMI_XCLK, "hdmi_xclk", "osc24m", TOP_CLK_GATE4, 26, 0, 0),
+ GATE(VIU_M0_ACLK, "viu_m0_aclk", "viu_m0_a_mux", TOP_CLK_GATE4, 0, CLK_SET_RATE_PARENT, 0),
+ GATE(VIU_M1_ACLK, "viu_m1_aclk", "viu_m1_a_mux", TOP_CLK_GATE4, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(VIU_WCLK, "viu_wclk", "viu_w_mux", TOP_CLK_GATE4, 2, CLK_SET_RATE_PARENT, 0),
+ GATE(VIU_JPEG_WCLK, "viu_jpeg_wclk", "viu_jpeg_w_mux", TOP_CLK_GATE4, 3, CLK_SET_RATE_PARENT, 0),
+ GATE(VIU_CFG_CLK, "viu_cfg_clk", "osc24m", TOP_CLK_GATE4, 6, 0, 0),
+ GATE(TS_SYS_WCLK, "ts_sys_wclk", "ts_sys_mux", TOP_CLK_GATE5, 2, CLK_SET_RATE_PARENT, 0),
+ GATE(TS_SYS_108M, "ts_sys_108m", "clk108m", TOP_CLK_GATE5, 3, 0, 0),
+ GATE(USB20_HCLK, "usb20_hclk", "sys_noc_hclk", TOP_CLK_GATE2, 12, 0, 0),
+ GATE(USB20_PHY_CLK, "usb20_phy_clk", "usb_ref24m_mux", TOP_CLK_GATE2, 13, 0, 0),
+ GATE(USB21_HCLK, "usb21_hclk", "sys_noc_hclk", TOP_CLK_GATE2, 14, 0, 0),
+ GATE(USB21_PHY_CLK, "usb21_phy_clk", "usb_ref24m_mux", TOP_CLK_GATE2, 15, 0, 0),
+ GATE(GMAC_RMIICLK, "gmac_rmii_clk", "clk50m", TOP_CLK_GATE2, 3, 0, 0),
+ GATE(GMAC_PCLK, "gmac_pclk", "clk198m", TOP_CLK_GATE2, 1, 0, 0),
+ GATE(GMAC_ACLK, "gmac_aclk", "clk49m5", TOP_CLK_GATE2, 0, 0, 0),
+ GATE(GMAC_RFCLK, "gmac_refclk", "clk25m", TOP_CLK_GATE2, 4, 0, 0),
+ GATE(SD1_AHB, "sd1_hclk", "sys_noc_hclk", TOP_CLK_GATE1, 12, 0, 0),
+ GATE(SD0_AHB, "sd0_hclk", "sys_noc_hclk", TOP_CLK_GATE1, 8, 0, 0),
+ GATE(TEMPSENSOR_GATE, "tempsensor_gate", "clk4m", TOP_CLK_GATE5, 31, 0, 0),
+};
+
+static struct clk_hw_onecell_data top_hw_onecell_data = {
+ .num = TOP_NR_CLKS,
+ .hws = {
+ [TOP_NR_CLKS - 1] = NULL,
+ },
+};
+
+static int __init top_clocks_init(struct device_node *np)
+{
+ void __iomem *reg_base;
+ int i, ret;
+
+ reg_base = of_iomap(np, 0);
+ if (!reg_base) {
+ pr_err("%s: Unable to map clk base\n", __func__);
+ return -ENXIO;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(zx296718_pll_clk); i++) {
+ zx296718_pll_clk[i].reg_base += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &zx296718_pll_clk[i].hw);
+ if (ret) {
+ pr_warn("top clk %s init error!\n",
+ zx296718_pll_clk[i].hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(top_ffactor_clk); i++) {
+ if (top_ffactor_clk[i].id)
+ top_hw_onecell_data.hws[top_ffactor_clk[i].id] =
+ &top_ffactor_clk[i].factor.hw;
+
+ ret = clk_hw_register(NULL, &top_ffactor_clk[i].factor.hw);
+ if (ret) {
+ pr_warn("top clk %s init error!\n",
+ top_ffactor_clk[i].factor.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(top_mux_clk); i++) {
+ if (top_mux_clk[i].id)
+ top_hw_onecell_data.hws[top_mux_clk[i].id] =
+ &top_mux_clk[i].mux.hw;
+
+ top_mux_clk[i].mux.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &top_mux_clk[i].mux.hw);
+ if (ret) {
+ pr_warn("top clk %s init error!\n",
+ top_mux_clk[i].mux.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(top_gate_clk); i++) {
+ if (top_gate_clk[i].id)
+ top_hw_onecell_data.hws[top_gate_clk[i].id] =
+ &top_gate_clk[i].gate.hw;
+
+ top_gate_clk[i].gate.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &top_gate_clk[i].gate.hw);
+ if (ret) {
+ pr_warn("top clk %s init error!\n",
+ top_gate_clk[i].gate.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(top_div_clk); i++) {
+ if (top_div_clk[i].id)
+ top_hw_onecell_data.hws[top_div_clk[i].id] =
+ &top_div_clk[i].div.hw;
+
+ top_div_clk[i].div.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &top_div_clk[i].div.hw);
+ if (ret) {
+ pr_warn("top clk %s init error!\n",
+ top_div_clk[i].div.hw.init->name);
+ }
+ }
+
+ if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &top_hw_onecell_data))
+ panic("could not register clk provider\n");
+ pr_info("top clk init over, nr:%d\n", TOP_NR_CLKS);
+
+ return 0;
+}
+
+static struct clk_div_table common_even_div_table[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 3, .div = 4, },
+ { .val = 5, .div = 6, },
+ { .val = 7, .div = 8, },
+ { .val = 9, .div = 10, },
+ { .val = 11, .div = 12, },
+ { .val = 13, .div = 14, },
+ { .val = 15, .div = 16, },
+};
+
+static struct clk_div_table common_div_table[] = {
+ { .val = 0, .div = 1, },
+ { .val = 1, .div = 2, },
+ { .val = 2, .div = 3, },
+ { .val = 3, .div = 4, },
+ { .val = 4, .div = 5, },
+ { .val = 5, .div = 6, },
+ { .val = 6, .div = 7, },
+ { .val = 7, .div = 8, },
+ { .val = 8, .div = 9, },
+ { .val = 9, .div = 10, },
+ { .val = 10, .div = 11, },
+ { .val = 11, .div = 12, },
+ { .val = 12, .div = 13, },
+ { .val = 13, .div = 14, },
+ { .val = 14, .div = 15, },
+ { .val = 15, .div = 16, },
+};
+
+PNAME(lsp0_wclk_common_p) = {
+ "lsp0_24m",
+ "lsp0_99m",
+};
+
+PNAME(lsp0_wclk_timer3_p) = {
+ "timer3_div",
+ "lsp0_32k"
+};
+
+PNAME(lsp0_wclk_timer4_p) = {
+ "timer4_div",
+ "lsp0_32k"
+};
+
+PNAME(lsp0_wclk_timer5_p) = {
+ "timer5_div",
+ "lsp0_32k"
+};
+
+PNAME(lsp0_wclk_spifc0_p) = {
+ "lsp0_148m5",
+ "lsp0_24m",
+ "lsp0_99m",
+ "lsp0_74m25"
+};
+
+PNAME(lsp0_wclk_ssp_p) = {
+ "lsp0_148m5",
+ "lsp0_99m",
+ "lsp0_24m",
+};
+
+static struct zx_clk_mux lsp0_mux_clk[] = {
+ MUX(0, "timer3_wclk_mux", lsp0_wclk_timer3_p, LSP0_TIMER3_CLK, 4, 1),
+ MUX(0, "timer4_wclk_mux", lsp0_wclk_timer4_p, LSP0_TIMER4_CLK, 4, 1),
+ MUX(0, "timer5_wclk_mux", lsp0_wclk_timer5_p, LSP0_TIMER5_CLK, 4, 1),
+ MUX(0, "uart3_wclk_mux", lsp0_wclk_common_p, LSP0_UART3_CLK, 4, 1),
+ MUX(0, "uart1_wclk_mux", lsp0_wclk_common_p, LSP0_UART1_CLK, 4, 1),
+ MUX(0, "uart2_wclk_mux", lsp0_wclk_common_p, LSP0_UART2_CLK, 4, 1),
+ MUX(0, "spifc0_wclk_mux", lsp0_wclk_spifc0_p, LSP0_SPIFC0_CLK, 4, 2),
+ MUX(0, "i2c4_wclk_mux", lsp0_wclk_common_p, LSP0_I2C4_CLK, 4, 1),
+ MUX(0, "i2c5_wclk_mux", lsp0_wclk_common_p, LSP0_I2C5_CLK, 4, 1),
+ MUX(0, "ssp0_wclk_mux", lsp0_wclk_ssp_p, LSP0_SSP0_CLK, 4, 1),
+ MUX(0, "ssp1_wclk_mux", lsp0_wclk_ssp_p, LSP0_SSP1_CLK, 4, 1),
+ MUX(0, "i2c3_wclk_mux", lsp0_wclk_common_p, LSP0_I2C3_CLK, 4, 1),
+};
+
+static struct zx_clk_gate lsp0_gate_clk[] = {
+ GATE(LSP0_TIMER3_WCLK, "timer3_wclk", "timer3_wclk_mux", LSP0_TIMER3_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_TIMER4_WCLK, "timer4_wclk", "timer4_wclk_mux", LSP0_TIMER4_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_TIMER5_WCLK, "timer5_wclk", "timer5_wclk_mux", LSP0_TIMER5_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_UART3_WCLK, "uart3_wclk", "uart3_wclk_mux", LSP0_UART3_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_UART1_WCLK, "uart1_wclk", "uart1_wclk_mux", LSP0_UART1_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_UART2_WCLK, "uart2_wclk", "uart2_wclk_mux", LSP0_UART2_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_SPIFC0_WCLK, "spifc0_wclk", "spifc0_wclk_mux", LSP0_SPIFC0_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_I2C4_WCLK, "i2c4_wclk", "i2c4_wclk_mux", LSP0_I2C4_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_I2C5_WCLK, "i2c5_wclk", "i2c5_wclk_mux", LSP0_I2C5_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_SSP0_WCLK, "ssp0_wclk", "ssp0_div", LSP0_SSP0_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_SSP1_WCLK, "ssp1_wclk", "ssp1_div", LSP0_SSP1_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP0_I2C3_WCLK, "i2c3_wclk", "i2c3_wclk_mux", LSP0_I2C3_CLK, 1, CLK_SET_RATE_PARENT, 0),
+};
+
+static struct zx_clk_div lsp0_div_clk[] = {
+ DIV_T(0, "timer3_div", "lsp0_24m", LSP0_TIMER3_CLK, 12, 4, 0, common_even_div_table),
+ DIV_T(0, "timer4_div", "lsp0_24m", LSP0_TIMER4_CLK, 12, 4, 0, common_even_div_table),
+ DIV_T(0, "timer5_div", "lsp0_24m", LSP0_TIMER5_CLK, 12, 4, 0, common_even_div_table),
+ DIV_T(0, "ssp0_div", "ssp0_wclk_mux", LSP0_SSP0_CLK, 12, 4, 0, common_even_div_table),
+ DIV_T(0, "ssp1_div", "ssp1_wclk_mux", LSP0_SSP1_CLK, 12, 4, 0, common_even_div_table),
+};
+
+static struct clk_hw_onecell_data lsp0_hw_onecell_data = {
+ .num = LSP0_NR_CLKS,
+ .hws = {
+ [LSP0_NR_CLKS - 1] = NULL,
+ },
+};
+
+static int __init lsp0_clocks_init(struct device_node *np)
+{
+ void __iomem *reg_base;
+ int i, ret;
+
+ reg_base = of_iomap(np, 0);
+ if (!reg_base) {
+ pr_err("%s: Unable to map clk base\n", __func__);
+ return -ENXIO;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lsp0_mux_clk); i++) {
+ if (lsp0_mux_clk[i].id)
+ lsp0_hw_onecell_data.hws[lsp0_mux_clk[i].id] =
+ &lsp0_mux_clk[i].mux.hw;
+
+ lsp0_mux_clk[i].mux.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &lsp0_mux_clk[i].mux.hw);
+ if (ret) {
+ pr_warn("lsp0 clk %s init error!\n",
+ lsp0_mux_clk[i].mux.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lsp0_gate_clk); i++) {
+ if (lsp0_gate_clk[i].id)
+ lsp0_hw_onecell_data.hws[lsp0_gate_clk[i].id] =
+ &lsp0_gate_clk[i].gate.hw;
+
+ lsp0_gate_clk[i].gate.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &lsp0_gate_clk[i].gate.hw);
+ if (ret) {
+ pr_warn("lsp0 clk %s init error!\n",
+ lsp0_gate_clk[i].gate.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lsp0_div_clk); i++) {
+ if (lsp0_div_clk[i].id)
+ lsp0_hw_onecell_data.hws[lsp0_div_clk[i].id] =
+ &lsp0_div_clk[i].div.hw;
+
+ lsp0_div_clk[i].div.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &lsp0_div_clk[i].div.hw);
+ if (ret) {
+ pr_warn("lsp0 clk %s init error!\n",
+ lsp0_div_clk[i].div.hw.init->name);
+ }
+ }
+
+ if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &lsp0_hw_onecell_data))
+ panic("could not register clk provider\n");
+ pr_info("lsp0-clk init over:%d\n", LSP0_NR_CLKS);
+
+ return 0;
+}
+
+PNAME(lsp1_wclk_common_p) = {
+ "lsp1_24m",
+ "lsp1_99m",
+};
+
+PNAME(lsp1_wclk_ssp_p) = {
+ "lsp1_148m5",
+ "lsp1_99m",
+ "lsp1_24m",
+};
+
+static struct zx_clk_mux lsp1_mux_clk[] = {
+ MUX(0, "uart4_wclk_mux", lsp1_wclk_common_p, LSP1_UART4_CLK, 4, 1),
+ MUX(0, "uart5_wclk_mux", lsp1_wclk_common_p, LSP1_UART5_CLK, 4, 1),
+ MUX(0, "pwm_wclk_mux", lsp1_wclk_common_p, LSP1_PWM_CLK, 4, 1),
+ MUX(0, "i2c2_wclk_mux", lsp1_wclk_common_p, LSP1_I2C2_CLK, 4, 1),
+ MUX(0, "ssp2_wclk_mux", lsp1_wclk_ssp_p, LSP1_SSP2_CLK, 4, 2),
+ MUX(0, "ssp3_wclk_mux", lsp1_wclk_ssp_p, LSP1_SSP3_CLK, 4, 2),
+ MUX(0, "ssp4_wclk_mux", lsp1_wclk_ssp_p, LSP1_SSP4_CLK, 4, 2),
+ MUX(0, "usim1_wclk_mux", lsp1_wclk_common_p, LSP1_USIM1_CLK, 4, 1),
+};
+
+static struct zx_clk_div lsp1_div_clk[] = {
+ DIV_T(0, "pwm_div", "pwm_wclk_mux", LSP1_PWM_CLK, 12, 4, CLK_SET_RATE_PARENT, common_div_table),
+ DIV_T(0, "ssp2_div", "ssp2_wclk_mux", LSP1_SSP2_CLK, 12, 4, CLK_SET_RATE_PARENT, common_even_div_table),
+ DIV_T(0, "ssp3_div", "ssp3_wclk_mux", LSP1_SSP3_CLK, 12, 4, CLK_SET_RATE_PARENT, common_even_div_table),
+ DIV_T(0, "ssp4_div", "ssp4_wclk_mux", LSP1_SSP4_CLK, 12, 4, CLK_SET_RATE_PARENT, common_even_div_table),
+};
+
+static struct zx_clk_gate lsp1_gate_clk[] = {
+ GATE(LSP1_UART4_WCLK, "lsp1_uart4_wclk", "uart4_wclk_mux", LSP1_UART4_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP1_UART5_WCLK, "lsp1_uart5_wclk", "uart5_wclk_mux", LSP1_UART5_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP1_PWM_WCLK, "lsp1_pwm_wclk", "pwm_div", LSP1_PWM_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP1_PWM_PCLK, "lsp1_pwm_pclk", "lsp1_pclk", LSP1_PWM_CLK, 0, 0, 0),
+ GATE(LSP1_I2C2_WCLK, "lsp1_i2c2_wclk", "i2c2_wclk_mux", LSP1_I2C2_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP1_SSP2_WCLK, "lsp1_ssp2_wclk", "ssp2_div", LSP1_SSP2_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP1_SSP3_WCLK, "lsp1_ssp3_wclk", "ssp3_div", LSP1_SSP3_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP1_SSP4_WCLK, "lsp1_ssp4_wclk", "ssp4_div", LSP1_SSP4_CLK, 1, CLK_SET_RATE_PARENT, 0),
+ GATE(LSP1_USIM1_WCLK, "lsp1_usim1_wclk", "usim1_wclk_mux", LSP1_USIM1_CLK, 1, CLK_SET_RATE_PARENT, 0),
+};
+
+static struct clk_hw_onecell_data lsp1_hw_onecell_data = {
+ .num = LSP1_NR_CLKS,
+ .hws = {
+ [LSP1_NR_CLKS - 1] = NULL,
+ },
+};
+
+static int __init lsp1_clocks_init(struct device_node *np)
+{
+ void __iomem *reg_base;
+ int i, ret;
+
+ reg_base = of_iomap(np, 0);
+ if (!reg_base) {
+ pr_err("%s: Unable to map clk base\n", __func__);
+ return -ENXIO;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lsp1_mux_clk); i++) {
+ if (lsp1_mux_clk[i].id)
+ lsp1_hw_onecell_data.hws[lsp1_mux_clk[i].id] =
+ &lsp0_mux_clk[i].mux.hw;
+
+ lsp1_mux_clk[i].mux.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &lsp1_mux_clk[i].mux.hw);
+ if (ret) {
+ pr_warn("lsp1 clk %s init error!\n",
+ lsp1_mux_clk[i].mux.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lsp1_gate_clk); i++) {
+ if (lsp1_gate_clk[i].id)
+ lsp1_hw_onecell_data.hws[lsp1_gate_clk[i].id] =
+ &lsp1_gate_clk[i].gate.hw;
+
+ lsp1_gate_clk[i].gate.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &lsp1_gate_clk[i].gate.hw);
+ if (ret) {
+ pr_warn("lsp1 clk %s init error!\n",
+ lsp1_gate_clk[i].gate.hw.init->name);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lsp1_div_clk); i++) {
+ if (lsp1_div_clk[i].id)
+ lsp1_hw_onecell_data.hws[lsp1_div_clk[i].id] =
+ &lsp1_div_clk[i].div.hw;
+
+ lsp1_div_clk[i].div.reg += (uintptr_t)reg_base;
+ ret = clk_hw_register(NULL, &lsp1_div_clk[i].div.hw);
+ if (ret) {
+ pr_warn("lsp1 clk %s init error!\n",
+ lsp1_div_clk[i].div.hw.init->name);
+ }
+ }
+
+ if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &lsp1_hw_onecell_data))
+ panic("could not register clk provider\n");
+ pr_info("lsp1-clk init over, nr:%d\n", LSP1_NR_CLKS);
+
+ return 0;
+}
+
+static const struct of_device_id zx_clkc_match_table[] = {
+ { .compatible = "zte,zx296718-topcrm", .data = &top_clocks_init },
+ { .compatible = "zte,zx296718-lsp0crm", .data = &lsp0_clocks_init },
+ { .compatible = "zte,zx296718-lsp1crm", .data = &lsp1_clocks_init },
+ { }
+};
+
+static int zx_clkc_probe(struct platform_device *pdev)
+{
+ int (*init_fn)(struct device_node *np);
+ struct device_node *np = pdev->dev.of_node;
+
+ init_fn = of_device_get_match_data(&pdev->dev);
+ if (!init_fn) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+
+ return init_fn(np);
+}
+
+static struct platform_driver zx_clk_driver = {
+ .probe = zx_clkc_probe,
+ .driver = {
+ .name = "zx296718-clkc",
+ .of_match_table = zx_clkc_match_table,
+ },
+};
+
+static int __init zx_clk_init(void)
+{
+ return platform_driver_register(&zx_clk_driver);
+}
+core_initcall(zx_clk_init);
diff --git a/drivers/clk/zte/clk.c b/drivers/clk/zte/clk.c
index 7c73c53..c4c1251 100644
--- a/drivers/clk/zte/clk.c
+++ b/drivers/clk/zte/clk.c
@@ -21,8 +21,8 @@
#define to_clk_zx_audio(_hw) container_of(_hw, struct clk_zx_audio, hw)
#define CFG0_CFG1_OFFSET 4
-#define LOCK_FLAG BIT(30)
-#define POWER_DOWN BIT(31)
+#define LOCK_FLAG 30
+#define POWER_DOWN 31
static int rate_to_idx(struct clk_zx_pll *zx_pll, unsigned long rate)
{
@@ -50,8 +50,8 @@ static int hw_to_idx(struct clk_zx_pll *zx_pll)
hw_cfg1 = readl_relaxed(zx_pll->reg_base + CFG0_CFG1_OFFSET);
/* For matching the value in lookup table */
- hw_cfg0 &= ~LOCK_FLAG;
- hw_cfg0 |= POWER_DOWN;
+ hw_cfg0 &= ~BIT(zx_pll->lock_bit);
+ hw_cfg0 |= BIT(zx_pll->pd_bit);
for (i = 0; i < zx_pll->count; i++) {
if (hw_cfg0 == config[i].cfg0 && hw_cfg1 == config[i].cfg1)
@@ -108,10 +108,10 @@ static int zx_pll_enable(struct clk_hw *hw)
u32 reg;
reg = readl_relaxed(zx_pll->reg_base);
- writel_relaxed(reg & ~POWER_DOWN, zx_pll->reg_base);
+ writel_relaxed(reg & ~BIT(zx_pll->pd_bit), zx_pll->reg_base);
return readl_relaxed_poll_timeout(zx_pll->reg_base, reg,
- reg & LOCK_FLAG, 0, 100);
+ reg & BIT(zx_pll->lock_bit), 0, 100);
}
static void zx_pll_disable(struct clk_hw *hw)
@@ -120,7 +120,7 @@ static void zx_pll_disable(struct clk_hw *hw)
u32 reg;
reg = readl_relaxed(zx_pll->reg_base);
- writel_relaxed(reg | POWER_DOWN, zx_pll->reg_base);
+ writel_relaxed(reg | BIT(zx_pll->pd_bit), zx_pll->reg_base);
}
static int zx_pll_is_enabled(struct clk_hw *hw)
@@ -130,10 +130,10 @@ static int zx_pll_is_enabled(struct clk_hw *hw)
reg = readl_relaxed(zx_pll->reg_base);
- return !(reg & POWER_DOWN);
+ return !(reg & BIT(zx_pll->pd_bit));
}
-static const struct clk_ops zx_pll_ops = {
+const struct clk_ops zx_pll_ops = {
.recalc_rate = zx_pll_recalc_rate,
.round_rate = zx_pll_round_rate,
.set_rate = zx_pll_set_rate,
@@ -141,6 +141,7 @@ static const struct clk_ops zx_pll_ops = {
.disable = zx_pll_disable,
.is_enabled = zx_pll_is_enabled,
};
+EXPORT_SYMBOL(zx_pll_ops);
struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
unsigned long flags, void __iomem *reg_base,
@@ -164,6 +165,8 @@ struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
zx_pll->reg_base = reg_base;
zx_pll->lookup_table = lookup_table;
zx_pll->count = count;
+ zx_pll->lock_bit = LOCK_FLAG;
+ zx_pll->pd_bit = POWER_DOWN;
zx_pll->lock = lock;
zx_pll->hw.init = &init;
diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h
index 65ae08b..0df3474 100644
--- a/drivers/clk/zte/clk.h
+++ b/drivers/clk/zte/clk.h
@@ -12,6 +12,26 @@
#include <linux/clk-provider.h>
#include <linux/spinlock.h>
+#define PNAME(x) static const char *x[]
+
+#define CLK_HW_INIT(_name, _parent, _ops, _flags) \
+ &(struct clk_init_data) { \
+ .flags = _flags, \
+ .name = _name, \
+ .parent_names = (const char *[]) { _parent }, \
+ .num_parents = 1, \
+ .ops = _ops, \
+ }
+
+#define CLK_HW_INIT_PARENTS(_name, _parents, _ops, _flags) \
+ &(struct clk_init_data) { \
+ .flags = _flags, \
+ .name = _name, \
+ .parent_names = _parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ .ops = _ops, \
+ }
+
struct zx_pll_config {
unsigned long rate;
u32 cfg0;
@@ -24,8 +44,115 @@ struct clk_zx_pll {
const struct zx_pll_config *lookup_table; /* order by rate asc */
int count;
spinlock_t *lock;
+ u8 pd_bit; /* power down bit */
+ u8 lock_bit; /* pll lock flag bit */
+};
+
+#define PLL_RATE(_rate, _cfg0, _cfg1) \
+{ \
+ .rate = _rate, \
+ .cfg0 = _cfg0, \
+ .cfg1 = _cfg1, \
+}
+
+#define ZX_PLL(_name, _parent, _reg, _table, _pd, _lock) \
+{ \
+ .reg_base = (void __iomem *) _reg, \
+ .lookup_table = _table, \
+ .count = ARRAY_SIZE(_table), \
+ .pd_bit = _pd, \
+ .lock_bit = _lock, \
+ .hw.init = CLK_HW_INIT(_name, _parent, &zx_pll_ops, \
+ CLK_GET_RATE_NOCACHE), \
+}
+
+#define ZX296718_PLL(_name, _parent, _reg, _table) \
+ZX_PLL(_name, _parent, _reg, _table, 0, 30)
+
+struct zx_clk_gate {
+ struct clk_gate gate;
+ u16 id;
+};
+
+#define GATE(_id, _name, _parent, _reg, _bit, _flag, _gflags) \
+{ \
+ .gate = { \
+ .reg = (void __iomem *) _reg, \
+ .bit_idx = (_bit), \
+ .flags = _gflags, \
+ .lock = &clk_lock, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &clk_gate_ops, \
+ _flag | CLK_IGNORE_UNUSED), \
+ }, \
+ .id = _id, \
+}
+
+struct zx_clk_fixed_factor {
+ struct clk_fixed_factor factor;
+ u16 id;
+};
+
+#define FFACTOR(_id, _name, _parent, _mult, _div, _flag) \
+{ \
+ .factor = { \
+ .div = _div, \
+ .mult = _mult, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &clk_fixed_factor_ops, \
+ _flag), \
+ }, \
+ .id = _id, \
+}
+
+struct zx_clk_mux {
+ struct clk_mux mux;
+ u16 id;
+};
+
+#define MUX_F(_id, _name, _parent, _reg, _shift, _width, _flag, _mflag) \
+{ \
+ .mux = { \
+ .reg = (void __iomem *) _reg, \
+ .mask = BIT(_width) - 1, \
+ .shift = _shift, \
+ .flags = _mflag, \
+ .lock = &clk_lock, \
+ .hw.init = CLK_HW_INIT_PARENTS(_name, \
+ _parent, \
+ &clk_mux_ops, \
+ _flag), \
+ }, \
+ .id = _id, \
+}
+
+#define MUX(_id, _name, _parent, _reg, _shift, _width) \
+MUX_F(_id, _name, _parent, _reg, _shift, _width, 0, 0)
+
+struct zx_clk_div {
+ struct clk_divider div;
+ u16 id;
};
+#define DIV_T(_id, _name, _parent, _reg, _shift, _width, _flag, _table) \
+{ \
+ .div = { \
+ .reg = (void __iomem *) _reg, \
+ .shift = _shift, \
+ .width = _width, \
+ .flags = 0, \
+ .table = _table, \
+ .lock = &clk_lock, \
+ .hw.init = CLK_HW_INIT(_name, \
+ _parent, \
+ &clk_divider_ops, \
+ _flag), \
+ }, \
+ .id = _id, \
+}
+
struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
unsigned long flags, void __iomem *reg_base,
const struct zx_pll_config *lookup_table, int count, spinlock_t *lock);
@@ -38,4 +165,6 @@ struct clk_zx_audio {
struct clk *clk_register_zx_audio(const char *name,
const char * const parent_name,
unsigned long flags, void __iomem *reg_base);
+
+extern const struct clk_ops zx_pll_ops;
#endif
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 5677886..8a753fd 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -305,6 +305,16 @@ config ARM_ARCH_TIMER_EVTSTREAM
This must be disabled for hardware validation purposes to detect any
hardware anomalies of missing events.
+config FSL_ERRATUM_A008585
+ bool "Workaround for Freescale/NXP Erratum A-008585"
+ default y
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a workaround for Freescale/NXP Erratum
+ A-008585 ("ARM generic timer may contain an erroneous
+ value"). The workaround will only be active if the
+ fsl,erratum-a008585 property is found in the timer node.
+
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
select CLKSRC_OF if OF
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 5770054..73c487d 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -94,6 +94,43 @@ early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
* Architected system timer support.
*/
+#ifdef CONFIG_FSL_ERRATUM_A008585
+DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
+EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+
+static int fsl_a008585_enable = -1;
+
+static int __init early_fsl_a008585_cfg(char *buf)
+{
+ int ret;
+ bool val;
+
+ ret = strtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ fsl_a008585_enable = val;
+ return 0;
+}
+early_param("clocksource.arm_arch_timer.fsl-a008585", early_fsl_a008585_cfg);
+
+u32 __fsl_a008585_read_cntp_tval_el0(void)
+{
+ return __fsl_a008585_read_reg(cntp_tval_el0);
+}
+
+u32 __fsl_a008585_read_cntv_tval_el0(void)
+{
+ return __fsl_a008585_read_reg(cntv_tval_el0);
+}
+
+u64 __fsl_a008585_read_cntvct_el0(void)
+{
+ return __fsl_a008585_read_reg(cntvct_el0);
+}
+EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
+#endif /* CONFIG_FSL_ERRATUM_A008585 */
+
static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
struct clock_event_device *clk)
@@ -243,6 +280,40 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
+#ifdef CONFIG_FSL_ERRATUM_A008585
+static __always_inline void fsl_a008585_set_next_event(const int access,
+ unsigned long evt, struct clock_event_device *clk)
+{
+ unsigned long ctrl;
+ u64 cval = evt + arch_counter_get_cntvct();
+
+ ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk);
+ ctrl |= ARCH_TIMER_CTRL_ENABLE;
+ ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
+
+ if (access == ARCH_TIMER_PHYS_ACCESS)
+ write_sysreg(cval, cntp_cval_el0);
+ else if (access == ARCH_TIMER_VIRT_ACCESS)
+ write_sysreg(cval, cntv_cval_el0);
+
+ arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
+}
+
+static int fsl_a008585_set_next_event_virt(unsigned long evt,
+ struct clock_event_device *clk)
+{
+ fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
+ return 0;
+}
+
+static int fsl_a008585_set_next_event_phys(unsigned long evt,
+ struct clock_event_device *clk)
+{
+ fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
+ return 0;
+}
+#endif /* CONFIG_FSL_ERRATUM_A008585 */
+
static int arch_timer_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
{
@@ -271,6 +342,19 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
return 0;
}
+static void fsl_a008585_set_sne(struct clock_event_device *clk)
+{
+#ifdef CONFIG_FSL_ERRATUM_A008585
+ if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
+ return;
+
+ if (arch_timer_uses_ppi == VIRT_PPI)
+ clk->set_next_event = fsl_a008585_set_next_event_virt;
+ else
+ clk->set_next_event = fsl_a008585_set_next_event_phys;
+#endif
+}
+
static void __arch_timer_setup(unsigned type,
struct clock_event_device *clk)
{
@@ -299,6 +383,8 @@ static void __arch_timer_setup(unsigned type,
default:
BUG();
}
+
+ fsl_a008585_set_sne(clk);
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
@@ -515,15 +601,19 @@ static void __init arch_counter_register(unsigned type)
arch_timer_read_counter = arch_counter_get_cntvct;
else
arch_timer_read_counter = arch_counter_get_cntpct;
- } else {
- arch_timer_read_counter = arch_counter_get_cntvct_mem;
- /* If the clocksource name is "arch_sys_counter" the
- * VDSO will attempt to read the CP15-based counter.
- * Ensure this does not happen when CP15-based
- * counter is not available.
+ clocksource_counter.archdata.vdso_direct = true;
+
+#ifdef CONFIG_FSL_ERRATUM_A008585
+ /*
+ * Don't use the vdso fastpath if errata require using
+ * the out-of-line counter accessor.
*/
- clocksource_counter.name = "arch_mem_counter";
+ if (static_branch_unlikely(&arch_timer_read_ool_enabled))
+ clocksource_counter.archdata.vdso_direct = false;
+#endif
+ } else {
+ arch_timer_read_counter = arch_counter_get_cntvct_mem;
}
start_count = arch_timer_read_counter();
@@ -800,6 +890,15 @@ static int __init arch_timer_of_init(struct device_node *np)
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
+#ifdef CONFIG_FSL_ERRATUM_A008585
+ if (fsl_a008585_enable < 0)
+ fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
+ if (fsl_a008585_enable) {
+ static_branch_enable(&arch_timer_read_ool_enabled);
+ pr_info("Enabling workaround for FSL erratum A-008585\n");
+ }
+#endif
+
/*
* If we cannot rely on firmware initializing the timer registers then
* we should use the physical timers instead.
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index b4b3ab5..7a960cd 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -109,12 +109,15 @@ static int gic_clockevent_init(void)
{
int ret;
- if (!cpu_has_counter || !gic_frequency)
+ if (!gic_frequency)
return -ENXIO;
ret = setup_percpu_irq(gic_timer_irq, &gic_compare_irqaction);
- if (ret < 0)
+ if (ret < 0) {
+ pr_err("GIC timer IRQ %d setup failed: %d\n",
+ gic_timer_irq, ret);
return ret;
+ }
cpuhp_setup_state(CPUHP_AP_MIPS_GIC_TIMER_STARTING,
"AP_MIPS_GIC_TIMER_STARTING", gic_starting_cpu,
diff --git a/drivers/clocksource/moxart_timer.c b/drivers/clocksource/moxart_timer.c
index 8414544..2a8f470 100644
--- a/drivers/clocksource/moxart_timer.c
+++ b/drivers/clocksource/moxart_timer.c
@@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/clocksource.h>
#include <linux/bitops.h>
+#include <linux/slab.h>
#define TIMER1_BASE 0x00
#define TIMER2_BASE 0x10
@@ -36,75 +37,109 @@
#define TIMER_INTR_MASK 0x38
/*
- * TIMER_CR flags:
+ * Moxart TIMER_CR flags:
*
- * TIMEREG_CR_*_CLOCK 0: PCLK, 1: EXT1CLK
- * TIMEREG_CR_*_INT overflow interrupt enable bit
+ * MOXART_CR_*_CLOCK 0: PCLK, 1: EXT1CLK
+ * MOXART_CR_*_INT overflow interrupt enable bit
*/
-#define TIMEREG_CR_1_ENABLE BIT(0)
-#define TIMEREG_CR_1_CLOCK BIT(1)
-#define TIMEREG_CR_1_INT BIT(2)
-#define TIMEREG_CR_2_ENABLE BIT(3)
-#define TIMEREG_CR_2_CLOCK BIT(4)
-#define TIMEREG_CR_2_INT BIT(5)
-#define TIMEREG_CR_3_ENABLE BIT(6)
-#define TIMEREG_CR_3_CLOCK BIT(7)
-#define TIMEREG_CR_3_INT BIT(8)
-#define TIMEREG_CR_COUNT_UP BIT(9)
-
-#define TIMER1_ENABLE (TIMEREG_CR_2_ENABLE | TIMEREG_CR_1_ENABLE)
-#define TIMER1_DISABLE (TIMEREG_CR_2_ENABLE)
-
-static void __iomem *base;
-static unsigned int clock_count_per_tick;
+#define MOXART_CR_1_ENABLE BIT(0)
+#define MOXART_CR_1_CLOCK BIT(1)
+#define MOXART_CR_1_INT BIT(2)
+#define MOXART_CR_2_ENABLE BIT(3)
+#define MOXART_CR_2_CLOCK BIT(4)
+#define MOXART_CR_2_INT BIT(5)
+#define MOXART_CR_3_ENABLE BIT(6)
+#define MOXART_CR_3_CLOCK BIT(7)
+#define MOXART_CR_3_INT BIT(8)
+#define MOXART_CR_COUNT_UP BIT(9)
+
+#define MOXART_TIMER1_ENABLE (MOXART_CR_2_ENABLE | MOXART_CR_1_ENABLE)
+#define MOXART_TIMER1_DISABLE (MOXART_CR_2_ENABLE)
+
+/*
+ * The ASpeed variant of the IP block has a different layout
+ * for the control register
+ */
+#define ASPEED_CR_1_ENABLE BIT(0)
+#define ASPEED_CR_1_CLOCK BIT(1)
+#define ASPEED_CR_1_INT BIT(2)
+#define ASPEED_CR_2_ENABLE BIT(4)
+#define ASPEED_CR_2_CLOCK BIT(5)
+#define ASPEED_CR_2_INT BIT(6)
+#define ASPEED_CR_3_ENABLE BIT(8)
+#define ASPEED_CR_3_CLOCK BIT(9)
+#define ASPEED_CR_3_INT BIT(10)
+
+#define ASPEED_TIMER1_ENABLE (ASPEED_CR_2_ENABLE | ASPEED_CR_1_ENABLE)
+#define ASPEED_TIMER1_DISABLE (ASPEED_CR_2_ENABLE)
+
+struct moxart_timer {
+ void __iomem *base;
+ unsigned int t1_disable_val;
+ unsigned int t1_enable_val;
+ unsigned int count_per_tick;
+ struct clock_event_device clkevt;
+};
+
+static inline struct moxart_timer *to_moxart(struct clock_event_device *evt)
+{
+ return container_of(evt, struct moxart_timer, clkevt);
+}
+
+static inline void moxart_disable(struct clock_event_device *evt)
+{
+ struct moxart_timer *timer = to_moxart(evt);
+
+ writel(timer->t1_disable_val, timer->base + TIMER_CR);
+}
+
+static inline void moxart_enable(struct clock_event_device *evt)
+{
+ struct moxart_timer *timer = to_moxart(evt);
+
+ writel(timer->t1_enable_val, timer->base + TIMER_CR);
+}
static int moxart_shutdown(struct clock_event_device *evt)
{
- writel(TIMER1_DISABLE, base + TIMER_CR);
+ moxart_disable(evt);
return 0;
}
static int moxart_set_oneshot(struct clock_event_device *evt)
{
- writel(TIMER1_DISABLE, base + TIMER_CR);
- writel(~0, base + TIMER1_BASE + REG_LOAD);
+ moxart_disable(evt);
+ writel(~0, to_moxart(evt)->base + TIMER1_BASE + REG_LOAD);
return 0;
}
static int moxart_set_periodic(struct clock_event_device *evt)
{
- writel(clock_count_per_tick, base + TIMER1_BASE + REG_LOAD);
- writel(TIMER1_ENABLE, base + TIMER_CR);
+ struct moxart_timer *timer = to_moxart(evt);
+
+ moxart_disable(evt);
+ writel(timer->count_per_tick, timer->base + TIMER1_BASE + REG_LOAD);
+ writel(0, timer->base + TIMER1_BASE + REG_MATCH1);
+ moxart_enable(evt);
return 0;
}
static int moxart_clkevt_next_event(unsigned long cycles,
- struct clock_event_device *unused)
+ struct clock_event_device *evt)
{
+ struct moxart_timer *timer = to_moxart(evt);
u32 u;
- writel(TIMER1_DISABLE, base + TIMER_CR);
+ moxart_disable(evt);
- u = readl(base + TIMER1_BASE + REG_COUNT) - cycles;
- writel(u, base + TIMER1_BASE + REG_MATCH1);
+ u = readl(timer->base + TIMER1_BASE + REG_COUNT) - cycles;
+ writel(u, timer->base + TIMER1_BASE + REG_MATCH1);
- writel(TIMER1_ENABLE, base + TIMER_CR);
+ moxart_enable(evt);
return 0;
}
-static struct clock_event_device moxart_clockevent = {
- .name = "moxart_timer",
- .rating = 200,
- .features = CLOCK_EVT_FEAT_PERIODIC |
- CLOCK_EVT_FEAT_ONESHOT,
- .set_state_shutdown = moxart_shutdown,
- .set_state_periodic = moxart_set_periodic,
- .set_state_oneshot = moxart_set_oneshot,
- .tick_resume = moxart_set_oneshot,
- .set_next_event = moxart_clkevt_next_event,
-};
-
static irqreturn_t moxart_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
@@ -112,21 +147,19 @@ static irqreturn_t moxart_timer_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct irqaction moxart_timer_irq = {
- .name = "moxart-timer",
- .flags = IRQF_TIMER,
- .handler = moxart_timer_interrupt,
- .dev_id = &moxart_clockevent,
-};
-
static int __init moxart_timer_init(struct device_node *node)
{
int ret, irq;
unsigned long pclk;
struct clk *clk;
+ struct moxart_timer *timer;
+
+ timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+ if (!timer)
+ return -ENOMEM;
- base = of_iomap(node, 0);
- if (!base) {
+ timer->base = of_iomap(node, 0);
+ if (!timer->base) {
pr_err("%s: of_iomap failed\n", node->full_name);
return -ENXIO;
}
@@ -137,12 +170,6 @@ static int __init moxart_timer_init(struct device_node *node)
return -EINVAL;
}
- ret = setup_irq(irq, &moxart_timer_irq);
- if (ret) {
- pr_err("%s: setup_irq failed\n", node->full_name);
- return ret;
- }
-
clk = of_clk_get(node, 0);
if (IS_ERR(clk)) {
pr_err("%s: of_clk_get failed\n", node->full_name);
@@ -151,7 +178,32 @@ static int __init moxart_timer_init(struct device_node *node)
pclk = clk_get_rate(clk);
- ret = clocksource_mmio_init(base + TIMER2_BASE + REG_COUNT,
+ if (of_device_is_compatible(node, "moxa,moxart-timer")) {
+ timer->t1_enable_val = MOXART_TIMER1_ENABLE;
+ timer->t1_disable_val = MOXART_TIMER1_DISABLE;
+ } else if (of_device_is_compatible(node, "aspeed,ast2400-timer")) {
+ timer->t1_enable_val = ASPEED_TIMER1_ENABLE;
+ timer->t1_disable_val = ASPEED_TIMER1_DISABLE;
+ } else {
+ pr_err("%s: unknown platform\n", node->full_name);
+ return -EINVAL;
+ }
+
+ timer->count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ);
+
+ timer->clkevt.name = node->name;
+ timer->clkevt.rating = 200;
+ timer->clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
+ CLOCK_EVT_FEAT_ONESHOT;
+ timer->clkevt.set_state_shutdown = moxart_shutdown;
+ timer->clkevt.set_state_periodic = moxart_set_periodic;
+ timer->clkevt.set_state_oneshot = moxart_set_oneshot;
+ timer->clkevt.tick_resume = moxart_set_oneshot;
+ timer->clkevt.set_next_event = moxart_clkevt_next_event;
+ timer->clkevt.cpumask = cpumask_of(0);
+ timer->clkevt.irq = irq;
+
+ ret = clocksource_mmio_init(timer->base + TIMER2_BASE + REG_COUNT,
"moxart_timer", pclk, 200, 32,
clocksource_mmio_readl_down);
if (ret) {
@@ -159,13 +211,26 @@ static int __init moxart_timer_init(struct device_node *node)
return ret;
}
- clock_count_per_tick = DIV_ROUND_CLOSEST(pclk, HZ);
+ ret = request_irq(irq, moxart_timer_interrupt, IRQF_TIMER,
+ node->name, &timer->clkevt);
+ if (ret) {
+ pr_err("%s: setup_irq failed\n", node->full_name);
+ return ret;
+ }
- writel(~0, base + TIMER2_BASE + REG_LOAD);
- writel(TIMEREG_CR_2_ENABLE, base + TIMER_CR);
+ /* Clear match registers */
+ writel(0, timer->base + TIMER1_BASE + REG_MATCH1);
+ writel(0, timer->base + TIMER1_BASE + REG_MATCH2);
+ writel(0, timer->base + TIMER2_BASE + REG_MATCH1);
+ writel(0, timer->base + TIMER2_BASE + REG_MATCH2);
- moxart_clockevent.cpumask = cpumask_of(0);
- moxart_clockevent.irq = irq;
+ /*
+ * Start timer 2 rolling as our main wall clock source, keep timer 1
+ * disabled
+ */
+ writel(0, timer->base + TIMER_CR);
+ writel(~0, timer->base + TIMER2_BASE + REG_LOAD);
+ writel(timer->t1_disable_val, timer->base + TIMER_CR);
/*
* documentation is not publicly available:
@@ -173,9 +238,9 @@ static int __init moxart_timer_init(struct device_node *node)
* max_delta 0xfffffffe should be ok because count
* register size is u32
*/
- clockevents_config_and_register(&moxart_clockevent, pclk,
- 0x4, 0xfffffffe);
+ clockevents_config_and_register(&timer->clkevt, pclk, 0x4, 0xfffffffe);
return 0;
}
CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init);
+CLOCKSOURCE_OF_DECLARE(aspeed, "aspeed,ast2400-timer", moxart_timer_init);
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
index 7f0f5b2..6555821 100644
--- a/drivers/clocksource/timer-atmel-pit.c
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -149,24 +149,13 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
{
struct pit_data *data = dev_id;
- /*
- * irqs should be disabled here, but as the irq is shared they are only
- * guaranteed to be off if the timer irq is registered first.
- */
- WARN_ON_ONCE(!irqs_disabled());
-
/* The PIT interrupt may be disabled, and is shared */
if (clockevent_state_periodic(&data->clkevt) &&
(pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) {
- unsigned nr_ticks;
-
/* Get number of ticks performed before irq, and ack it */
- nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR));
- do {
- data->cnt += data->cycle;
- data->clkevt.event_handler(&data->clkevt);
- nr_ticks--;
- } while (nr_ticks);
+ data->cnt += data->cycle * PIT_PICNT(pit_read(data->base,
+ AT91_PIT_PIVR));
+ data->clkevt.event_handler(&data->clkevt);
return IRQ_HANDLED;
}
@@ -177,11 +166,41 @@ static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id)
/*
* Set up both clocksource and clockevent support.
*/
-static int __init at91sam926x_pit_common_init(struct pit_data *data)
+static int __init at91sam926x_pit_dt_init(struct device_node *node)
{
- unsigned long pit_rate;
- unsigned bits;
- int ret;
+ unsigned long pit_rate;
+ unsigned bits;
+ int ret;
+ struct pit_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->base = of_iomap(node, 0);
+ if (!data->base) {
+ pr_err("Could not map PIT address\n");
+ return -ENXIO;
+ }
+
+ data->mck = of_clk_get(node, 0);
+ if (IS_ERR(data->mck)) {
+ pr_err("Unable to get mck clk\n");
+ return PTR_ERR(data->mck);
+ }
+
+ ret = clk_prepare_enable(data->mck);
+ if (ret) {
+ pr_err("Unable to enable mck\n");
+ return ret;
+ }
+
+ /* Get the interrupts property */
+ data->irq = irq_of_parse_and_map(node, 0);
+ if (!data->irq) {
+ pr_err("Unable to get IRQ from DT\n");
+ return -EINVAL;
+ }
/*
* Use our actual MCK to figure out how many MCK/16 ticks per
@@ -236,46 +255,5 @@ static int __init at91sam926x_pit_common_init(struct pit_data *data)
return 0;
}
-
-static int __init at91sam926x_pit_dt_init(struct device_node *node)
-{
- struct pit_data *data;
- int ret;
-
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- data->base = of_iomap(node, 0);
- if (!data->base) {
- pr_err("Could not map PIT address\n");
- return -ENXIO;
- }
-
- data->mck = of_clk_get(node, 0);
- if (IS_ERR(data->mck))
- /* Fallback on clkdev for !CCF-based boards */
- data->mck = clk_get(NULL, "mck");
-
- if (IS_ERR(data->mck)) {
- pr_err("Unable to get mck clk\n");
- return PTR_ERR(data->mck);
- }
-
- ret = clk_prepare_enable(data->mck);
- if (ret) {
- pr_err("Unable to enable mck\n");
- return ret;
- }
-
- /* Get the interrupts property */
- data->irq = irq_of_parse_and_map(node, 0);
- if (!data->irq) {
- pr_err("Unable to get IRQ from DT\n");
- return -EINVAL;
- }
-
- return at91sam926x_pit_common_init(data);
-}
CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
at91sam926x_pit_dt_init);
diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c
index bd887e2..d630bf4 100644
--- a/drivers/clocksource/timer-oxnas-rps.c
+++ b/drivers/clocksource/timer-oxnas-rps.c
@@ -295,3 +295,5 @@ err_alloc:
CLOCKSOURCE_OF_DECLARE(ox810se_rps,
"oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
+CLOCKSOURCE_OF_DECLARE(ox820_rps,
+ "oxsemi,ox820se-rps-timer", oxnas_rps_timer_init);
diff --git a/drivers/clocksource/timer-ti-32k.c b/drivers/clocksource/timer-ti-32k.c
index 92b7e39..cf5b14e 100644
--- a/drivers/clocksource/timer-ti-32k.c
+++ b/drivers/clocksource/timer-ti-32k.c
@@ -65,7 +65,7 @@ static inline struct ti_32k *to_ti_32k(struct clocksource *cs)
return container_of(cs, struct ti_32k, cs);
}
-static cycle_t ti_32k_read_cycles(struct clocksource *cs)
+static cycle_t notrace ti_32k_read_cycles(struct clocksource *cs)
{
struct ti_32k *ti = to_ti_32k(cs);
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 74919aa..d8b164a 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -194,7 +194,7 @@ config CPU_FREQ_GOV_CONSERVATIVE
If in doubt, say N.
config CPU_FREQ_GOV_SCHEDUTIL
- tristate "'schedutil' cpufreq policy governor"
+ bool "'schedutil' cpufreq policy governor"
depends on CPU_FREQ && SMP
select CPU_FREQ_GOV_ATTR_SET
select IRQ_WORK
@@ -208,9 +208,6 @@ config CPU_FREQ_GOV_SCHEDUTIL
frequency tipping point is at utilization/capacity equal to 80% in
both cases.
- To compile this driver as a module, choose M here: the module will
- be called cpufreq_schedutil.
-
If in doubt, say N.
comment "CPU frequency scaling drivers"
@@ -225,7 +222,7 @@ config CPUFREQ_DT
help
This adds a generic DT based cpufreq driver for frequency management.
It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
- systems which share clock and voltage across all CPUs.
+ systems.
If in doubt, say N.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 8882b8e..1b2f28f 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -19,10 +19,19 @@
#include <linux/delay.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
+#include <linux/dmi.h>
#include <linux/vmalloc.h>
+#include <asm/unaligned.h>
+
#include <acpi/cppc_acpi.h>
+/* Minimum struct length needed for the DMI processor entry we want */
+#define DMI_ENTRY_PROCESSOR_MIN_LENGTH 48
+
+/* Offest in the DMI processor structure for the max frequency */
+#define DMI_PROCESSOR_MAX_SPEED 0x14
+
/*
* These structs contain information parsed from per CPU
* ACPI _CPC structures.
@@ -30,19 +39,52 @@
* performance capabilities, desired performance level
* requested etc.
*/
-static struct cpudata **all_cpu_data;
+static struct cppc_cpudata **all_cpu_data;
+
+/* Capture the max KHz from DMI */
+static u64 cppc_dmi_max_khz;
+
+/* Callback function used to retrieve the max frequency from DMI */
+static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
+{
+ const u8 *dmi_data = (const u8 *)dm;
+ u16 *mhz = (u16 *)private;
+
+ if (dm->type == DMI_ENTRY_PROCESSOR &&
+ dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) {
+ u16 val = (u16)get_unaligned((const u16 *)
+ (dmi_data + DMI_PROCESSOR_MAX_SPEED));
+ *mhz = val > *mhz ? val : *mhz;
+ }
+}
+
+/* Look up the max frequency in DMI */
+static u64 cppc_get_dmi_max_khz(void)
+{
+ u16 mhz = 0;
+
+ dmi_walk(cppc_find_dmi_mhz, &mhz);
+
+ /*
+ * Real stupid fallback value, just in case there is no
+ * actual value set.
+ */
+ mhz = mhz ? mhz : 1;
+
+ return (1000 * mhz);
+}
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
- struct cpudata *cpu;
+ struct cppc_cpudata *cpu;
struct cpufreq_freqs freqs;
int ret = 0;
cpu = all_cpu_data[policy->cpu];
- cpu->perf_ctrls.desired_perf = target_freq;
+ cpu->perf_ctrls.desired_perf = (u64)target_freq * policy->max / cppc_dmi_max_khz;
freqs.old = policy->cur;
freqs.new = target_freq;
@@ -66,7 +108,7 @@ static int cppc_verify_policy(struct cpufreq_policy *policy)
static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
{
int cpu_num = policy->cpu;
- struct cpudata *cpu = all_cpu_data[cpu_num];
+ struct cppc_cpudata *cpu = all_cpu_data[cpu_num];
int ret;
cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf;
@@ -79,7 +121,7 @@ static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
- struct cpudata *cpu;
+ struct cppc_cpudata *cpu;
unsigned int cpu_num = policy->cpu;
int ret = 0;
@@ -94,10 +136,13 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
return ret;
}
- policy->min = cpu->perf_caps.lowest_perf;
- policy->max = cpu->perf_caps.highest_perf;
+ cppc_dmi_max_khz = cppc_get_dmi_max_khz();
+
+ policy->min = cpu->perf_caps.lowest_perf * cppc_dmi_max_khz / cpu->perf_caps.highest_perf;
+ policy->max = cppc_dmi_max_khz;
policy->cpuinfo.min_freq = policy->min;
policy->cpuinfo.max_freq = policy->max;
+ policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num);
policy->shared_type = cpu->shared_type;
if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
@@ -112,7 +157,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
cpu->cur_policy = policy;
/* Set policy->cur to max now. The governors will adjust later. */
- policy->cur = cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
+ policy->cur = cppc_dmi_max_khz;
+ cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
if (ret)
@@ -134,7 +180,7 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
static int __init cppc_cpufreq_init(void)
{
int i, ret = 0;
- struct cpudata *cpu;
+ struct cppc_cpudata *cpu;
if (acpi_disabled)
return -ENODEV;
@@ -144,7 +190,7 @@ static int __init cppc_cpufreq_init(void)
return -ENOMEM;
for_each_possible_cpu(i) {
- all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL);
+ all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL);
if (!all_cpu_data[i])
goto out;
@@ -175,7 +221,7 @@ out:
static void __exit cppc_cpufreq_exit(void)
{
- struct cpudata *cpu;
+ struct cppc_cpudata *cpu;
int i;
cpufreq_unregister_driver(&cppc_cpufreq_driver);
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index 2ee40fd..7126762 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -11,6 +11,8 @@
#include <linux/of.h>
#include <linux/platform_device.h>
+#include "cpufreq-dt.h"
+
static const struct of_device_id machines[] __initconst = {
{ .compatible = "allwinner,sun4i-a10", },
{ .compatible = "allwinner,sun5i-a10s", },
@@ -40,6 +42,7 @@ static const struct of_device_id machines[] __initconst = {
{ .compatible = "samsung,exynos5250", },
#ifndef CONFIG_BL_SWITCHER
{ .compatible = "samsung,exynos5420", },
+ { .compatible = "samsung,exynos5433", },
{ .compatible = "samsung,exynos5800", },
#endif
@@ -51,6 +54,7 @@ static const struct of_device_id machines[] __initconst = {
{ .compatible = "renesas,r8a7779", },
{ .compatible = "renesas,r8a7790", },
{ .compatible = "renesas,r8a7791", },
+ { .compatible = "renesas,r8a7792", },
{ .compatible = "renesas,r8a7793", },
{ .compatible = "renesas,r8a7794", },
{ .compatible = "renesas,sh73a0", },
@@ -68,6 +72,8 @@ static const struct of_device_id machines[] __initconst = {
{ .compatible = "sigma,tango4" },
+ { .compatible = "ti,am33xx", },
+ { .compatible = "ti,dra7", },
{ .compatible = "ti,omap2", },
{ .compatible = "ti,omap3", },
{ .compatible = "ti,omap4", },
@@ -91,7 +97,8 @@ static int __init cpufreq_dt_platdev_init(void)
if (!match)
return -ENODEV;
- return PTR_ERR_OR_ZERO(platform_device_register_simple("cpufreq-dt", -1,
- NULL, 0));
+ return PTR_ERR_OR_ZERO(platform_device_register_data(NULL, "cpufreq-dt",
+ -1, match->data,
+ sizeof(struct cpufreq_dt_platform_data)));
}
device_initcall(cpufreq_dt_platdev_init);
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 3957de8..5c07ae0 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -25,6 +25,8 @@
#include <linux/slab.h>
#include <linux/thermal.h>
+#include "cpufreq-dt.h"
+
struct private_data {
struct device *cpu_dev;
struct thermal_cooling_device *cdev;
@@ -353,6 +355,7 @@ static struct cpufreq_driver dt_cpufreq_driver = {
static int dt_cpufreq_probe(struct platform_device *pdev)
{
+ struct cpufreq_dt_platform_data *data = dev_get_platdata(&pdev->dev);
int ret;
/*
@@ -366,7 +369,8 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
if (ret)
return ret;
- dt_cpufreq_driver.driver_data = dev_get_platdata(&pdev->dev);
+ if (data && data->have_governor_per_policy)
+ dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
ret = cpufreq_register_driver(&dt_cpufreq_driver);
if (ret)
diff --git a/drivers/cpufreq/cpufreq-dt.h b/drivers/cpufreq/cpufreq-dt.h
new file mode 100644
index 0000000..54d774e
--- /dev/null
+++ b/drivers/cpufreq/cpufreq-dt.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 Linaro
+ * Viresh Kumar <viresh.kumar@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CPUFREQ_DT_H__
+#define __CPUFREQ_DT_H__
+
+#include <linux/types.h>
+
+struct cpufreq_dt_platform_data {
+ bool have_governor_per_policy;
+};
+
+#endif /* __CPUFREQ_DT_H__ */
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 3dd4884..6e6c1fb 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -916,58 +916,18 @@ static struct kobj_type ktype_cpufreq = {
.release = cpufreq_sysfs_release,
};
-static int add_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu)
+static int add_cpu_dev_symlink(struct cpufreq_policy *policy,
+ struct device *dev)
{
- struct device *cpu_dev;
-
- pr_debug("%s: Adding symlink for CPU: %u\n", __func__, cpu);
-
- if (!policy)
- return 0;
-
- cpu_dev = get_cpu_device(cpu);
- if (WARN_ON(!cpu_dev))
- return 0;
-
- return sysfs_create_link(&cpu_dev->kobj, &policy->kobj, "cpufreq");
-}
-
-static void remove_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu)
-{
- struct device *cpu_dev;
-
- pr_debug("%s: Removing symlink for CPU: %u\n", __func__, cpu);
-
- cpu_dev = get_cpu_device(cpu);
- if (WARN_ON(!cpu_dev))
- return;
-
- sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
-}
-
-/* Add/remove symlinks for all related CPUs */
-static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy)
-{
- unsigned int j;
- int ret = 0;
-
- /* Some related CPUs might not be present (physically hotplugged) */
- for_each_cpu(j, policy->real_cpus) {
- ret = add_cpu_dev_symlink(policy, j);
- if (ret)
- break;
- }
-
- return ret;
+ dev_dbg(dev, "%s: Adding symlink\n", __func__);
+ return sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
}
-static void cpufreq_remove_dev_symlink(struct cpufreq_policy *policy)
+static void remove_cpu_dev_symlink(struct cpufreq_policy *policy,
+ struct device *dev)
{
- unsigned int j;
-
- /* Some related CPUs might not be present (physically hotplugged) */
- for_each_cpu(j, policy->real_cpus)
- remove_cpu_dev_symlink(policy, j);
+ dev_dbg(dev, "%s: Removing symlink\n", __func__);
+ sysfs_remove_link(&dev->kobj, "cpufreq");
}
static int cpufreq_add_dev_interface(struct cpufreq_policy *policy)
@@ -999,7 +959,7 @@ static int cpufreq_add_dev_interface(struct cpufreq_policy *policy)
return ret;
}
- return cpufreq_add_dev_symlink(policy);
+ return 0;
}
__weak struct cpufreq_governor *cpufreq_default_governor(void)
@@ -1073,13 +1033,9 @@ static void handle_update(struct work_struct *work)
static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
{
- struct device *dev = get_cpu_device(cpu);
struct cpufreq_policy *policy;
int ret;
- if (WARN_ON(!dev))
- return NULL;
-
policy = kzalloc(sizeof(*policy), GFP_KERNEL);
if (!policy)
return NULL;
@@ -1133,7 +1089,6 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify)
down_write(&policy->rwsem);
cpufreq_stats_free_table(policy);
- cpufreq_remove_dev_symlink(policy);
kobj = &policy->kobj;
cmp = &policy->kobj_unregister;
up_write(&policy->rwsem);
@@ -1215,8 +1170,8 @@ static int cpufreq_online(unsigned int cpu)
if (new_policy) {
/* related_cpus should at least include policy->cpus. */
cpumask_copy(policy->related_cpus, policy->cpus);
- /* Remember CPUs present at the policy creation time. */
- cpumask_and(policy->real_cpus, policy->cpus, cpu_present_mask);
+ /* Clear mask of registered CPUs */
+ cpumask_clear(policy->real_cpus);
}
/*
@@ -1331,6 +1286,8 @@ out_free_policy:
return ret;
}
+static int cpufreq_offline(unsigned int cpu);
+
/**
* cpufreq_add_dev - the cpufreq interface for a CPU device.
* @dev: CPU device.
@@ -1340,25 +1297,31 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{
struct cpufreq_policy *policy;
unsigned cpu = dev->id;
+ int ret;
dev_dbg(dev, "%s: adding CPU%u\n", __func__, cpu);
- if (cpu_online(cpu))
- return cpufreq_online(cpu);
+ if (cpu_online(cpu)) {
+ ret = cpufreq_online(cpu);
+ if (ret)
+ return ret;
+ }
- /*
- * A hotplug notifier will follow and we will handle it as CPU online
- * then. For now, just create the sysfs link, unless there is no policy
- * or the link is already present.
- */
+ /* Create sysfs link on CPU registration */
policy = per_cpu(cpufreq_cpu_data, cpu);
if (!policy || cpumask_test_and_set_cpu(cpu, policy->real_cpus))
return 0;
- return add_cpu_dev_symlink(policy, cpu);
+ ret = add_cpu_dev_symlink(policy, dev);
+ if (ret) {
+ cpumask_clear_cpu(cpu, policy->real_cpus);
+ cpufreq_offline(cpu);
+ }
+
+ return ret;
}
-static void cpufreq_offline(unsigned int cpu)
+static int cpufreq_offline(unsigned int cpu)
{
struct cpufreq_policy *policy;
int ret;
@@ -1368,7 +1331,7 @@ static void cpufreq_offline(unsigned int cpu)
policy = cpufreq_cpu_get_raw(cpu);
if (!policy) {
pr_debug("%s: No cpu_data found\n", __func__);
- return;
+ return 0;
}
down_write(&policy->rwsem);
@@ -1417,6 +1380,7 @@ static void cpufreq_offline(unsigned int cpu)
unlock:
up_write(&policy->rwsem);
+ return 0;
}
/**
@@ -1436,7 +1400,7 @@ static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
cpufreq_offline(cpu);
cpumask_clear_cpu(cpu, policy->real_cpus);
- remove_cpu_dev_symlink(policy, cpu);
+ remove_cpu_dev_symlink(policy, dev);
if (cpumask_empty(policy->real_cpus))
cpufreq_policy_free(policy, true);
@@ -2332,28 +2296,6 @@ unlock:
}
EXPORT_SYMBOL(cpufreq_update_policy);
-static int cpufreq_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
-{
- unsigned int cpu = (unsigned long)hcpu;
-
- switch (action & ~CPU_TASKS_FROZEN) {
- case CPU_ONLINE:
- case CPU_DOWN_FAILED:
- cpufreq_online(cpu);
- break;
-
- case CPU_DOWN_PREPARE:
- cpufreq_offline(cpu);
- break;
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block __refdata cpufreq_cpu_notifier = {
- .notifier_call = cpufreq_cpu_callback,
-};
-
/*********************************************************************
* BOOST *
*********************************************************************/
@@ -2455,6 +2397,7 @@ EXPORT_SYMBOL_GPL(cpufreq_boost_enabled);
/*********************************************************************
* REGISTER / UNREGISTER CPUFREQ DRIVER *
*********************************************************************/
+static enum cpuhp_state hp_online;
/**
* cpufreq_register_driver - register a CPU Frequency driver
@@ -2517,7 +2460,14 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
goto err_if_unreg;
}
- register_hotcpu_notifier(&cpufreq_cpu_notifier);
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "cpufreq:online",
+ cpufreq_online,
+ cpufreq_offline);
+ if (ret < 0)
+ goto err_if_unreg;
+ hp_online = ret;
+ ret = 0;
+
pr_debug("driver %s up and running\n", driver_data->name);
goto out;
@@ -2556,7 +2506,7 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
get_online_cpus();
subsys_interface_unregister(&cpufreq_interface);
remove_boost_sysfs_file();
- unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
+ cpuhp_remove_state_nocalls(hp_online);
write_lock_irqsave(&cpufreq_driver_lock, flags);
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index e415349..642dd0f 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -260,7 +260,7 @@ static void dbs_irq_work(struct irq_work *irq_work)
}
static void dbs_update_util_handler(struct update_util_data *data, u64 time,
- unsigned long util, unsigned long max)
+ unsigned int flags)
{
struct cpu_dbs_info *cdbs = container_of(data, struct cpu_dbs_info, update_util);
struct policy_dbs_info *policy_dbs = cdbs->policy_dbs;
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index be9eade..806f203 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -181,6 +181,8 @@ struct _pid {
* @cpu: CPU number for this instance data
* @update_util: CPUFreq utility callback information
* @update_util_set: CPUFreq utility callback is set
+ * @iowait_boost: iowait-related boost fraction
+ * @last_update: Time of the last update.
* @pstate: Stores P state limits for this CPU
* @vid: Stores VID limits for this CPU
* @pid: Stores PID parameters for this CPU
@@ -206,6 +208,7 @@ struct cpudata {
struct vid_data vid;
struct _pid pid;
+ u64 last_update;
u64 last_sample_time;
u64 prev_aperf;
u64 prev_mperf;
@@ -216,6 +219,7 @@ struct cpudata {
struct acpi_processor_performance acpi_perf_data;
bool valid_pss_table;
#endif
+ unsigned int iowait_boost;
};
static struct cpudata **all_cpu_data;
@@ -229,6 +233,7 @@ static struct cpudata **all_cpu_data;
* @p_gain_pct: PID proportional gain
* @i_gain_pct: PID integral gain
* @d_gain_pct: PID derivative gain
+ * @boost_iowait: Whether or not to use iowait boosting.
*
* Stores per CPU model static PID configuration data.
*/
@@ -240,6 +245,7 @@ struct pstate_adjust_policy {
int p_gain_pct;
int d_gain_pct;
int i_gain_pct;
+ bool boost_iowait;
};
/**
@@ -1029,7 +1035,7 @@ static struct cpu_defaults core_params = {
},
};
-static struct cpu_defaults silvermont_params = {
+static const struct cpu_defaults silvermont_params = {
.pid_policy = {
.sample_rate_ms = 10,
.deadband = 0,
@@ -1037,6 +1043,7 @@ static struct cpu_defaults silvermont_params = {
.p_gain_pct = 14,
.d_gain_pct = 0,
.i_gain_pct = 4,
+ .boost_iowait = true,
},
.funcs = {
.get_max = atom_get_max_pstate,
@@ -1050,7 +1057,7 @@ static struct cpu_defaults silvermont_params = {
},
};
-static struct cpu_defaults airmont_params = {
+static const struct cpu_defaults airmont_params = {
.pid_policy = {
.sample_rate_ms = 10,
.deadband = 0,
@@ -1058,6 +1065,7 @@ static struct cpu_defaults airmont_params = {
.p_gain_pct = 14,
.d_gain_pct = 0,
.i_gain_pct = 4,
+ .boost_iowait = true,
},
.funcs = {
.get_max = atom_get_max_pstate,
@@ -1071,7 +1079,7 @@ static struct cpu_defaults airmont_params = {
},
};
-static struct cpu_defaults knl_params = {
+static const struct cpu_defaults knl_params = {
.pid_policy = {
.sample_rate_ms = 10,
.deadband = 0,
@@ -1091,7 +1099,7 @@ static struct cpu_defaults knl_params = {
},
};
-static struct cpu_defaults bxt_params = {
+static const struct cpu_defaults bxt_params = {
.pid_policy = {
.sample_rate_ms = 10,
.deadband = 0,
@@ -1099,6 +1107,7 @@ static struct cpu_defaults bxt_params = {
.p_gain_pct = 14,
.d_gain_pct = 0,
.i_gain_pct = 4,
+ .boost_iowait = true,
},
.funcs = {
.get_max = core_get_max_pstate,
@@ -1222,36 +1231,18 @@ static inline int32_t get_avg_pstate(struct cpudata *cpu)
static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu)
{
struct sample *sample = &cpu->sample;
- u64 cummulative_iowait, delta_iowait_us;
- u64 delta_iowait_mperf;
- u64 mperf, now;
- int32_t cpu_load;
+ int32_t busy_frac, boost;
- cummulative_iowait = get_cpu_iowait_time_us(cpu->cpu, &now);
+ busy_frac = div_fp(sample->mperf, sample->tsc);
- /*
- * Convert iowait time into number of IO cycles spent at max_freq.
- * IO is considered as busy only for the cpu_load algorithm. For
- * performance this is not needed since we always try to reach the
- * maximum P-State, so we are already boosting the IOs.
- */
- delta_iowait_us = cummulative_iowait - cpu->prev_cummulative_iowait;
- delta_iowait_mperf = div64_u64(delta_iowait_us * cpu->pstate.scaling *
- cpu->pstate.max_pstate, MSEC_PER_SEC);
+ boost = cpu->iowait_boost;
+ cpu->iowait_boost >>= 1;
- mperf = cpu->sample.mperf + delta_iowait_mperf;
- cpu->prev_cummulative_iowait = cummulative_iowait;
+ if (busy_frac < boost)
+ busy_frac = boost;
- /*
- * The load can be estimated as the ratio of the mperf counter
- * running at a constant frequency during active periods
- * (C0) and the time stamp counter running at the same frequency
- * also during C-states.
- */
- cpu_load = div64_u64(int_tofp(100) * mperf, sample->tsc);
- cpu->sample.busy_scaled = cpu_load;
-
- return get_avg_pstate(cpu) - pid_calc(&cpu->pid, cpu_load);
+ sample->busy_scaled = busy_frac * 100;
+ return get_avg_pstate(cpu) - pid_calc(&cpu->pid, sample->busy_scaled);
}
static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu)
@@ -1325,15 +1316,29 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu)
sample->mperf,
sample->aperf,
sample->tsc,
- get_avg_frequency(cpu));
+ get_avg_frequency(cpu),
+ fp_toint(cpu->iowait_boost * 100));
}
static void intel_pstate_update_util(struct update_util_data *data, u64 time,
- unsigned long util, unsigned long max)
+ unsigned int flags)
{
struct cpudata *cpu = container_of(data, struct cpudata, update_util);
- u64 delta_ns = time - cpu->sample.time;
+ u64 delta_ns;
+
+ if (pid_params.boost_iowait) {
+ if (flags & SCHED_CPUFREQ_IOWAIT) {
+ cpu->iowait_boost = int_tofp(1);
+ } else if (cpu->iowait_boost) {
+ /* Clear iowait_boost if the CPU may have been idle. */
+ delta_ns = time - cpu->last_update;
+ if (delta_ns > TICK_NSEC)
+ cpu->iowait_boost = 0;
+ }
+ cpu->last_update = time;
+ }
+ delta_ns = time - cpu->sample.time;
if ((s64)delta_ns >= pid_params.sample_rate_ns) {
bool sample_taken = intel_pstate_sample(cpu, time);
diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c
index be42f10..1b9bcd7 100644
--- a/drivers/cpufreq/kirkwood-cpufreq.c
+++ b/drivers/cpufreq/kirkwood-cpufreq.c
@@ -123,7 +123,7 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
priv.cpu_clk = of_clk_get_by_name(np, "cpu_clk");
if (IS_ERR(priv.cpu_clk)) {
- dev_err(priv.dev, "Unable to get cpuclk");
+ dev_err(priv.dev, "Unable to get cpuclk\n");
return PTR_ERR(priv.cpu_clk);
}
@@ -132,7 +132,7 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
priv.ddr_clk = of_clk_get_by_name(np, "ddrclk");
if (IS_ERR(priv.ddr_clk)) {
- dev_err(priv.dev, "Unable to get ddrclk");
+ dev_err(priv.dev, "Unable to get ddrclk\n");
err = PTR_ERR(priv.ddr_clk);
goto out_cpu;
}
@@ -142,7 +142,7 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
priv.powersave_clk = of_clk_get_by_name(np, "powersave");
if (IS_ERR(priv.powersave_clk)) {
- dev_err(priv.dev, "Unable to get powersave");
+ dev_err(priv.dev, "Unable to get powersave\n");
err = PTR_ERR(priv.powersave_clk);
goto out_ddr;
}
@@ -155,7 +155,7 @@ static int kirkwood_cpufreq_probe(struct platform_device *pdev)
if (!err)
return 0;
- dev_err(priv.dev, "Failed to register cpufreq driver");
+ dev_err(priv.dev, "Failed to register cpufreq driver\n");
clk_disable_unprepare(priv.powersave_clk);
out_ddr:
diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c
index e8a7bf5..ea7a4e1 100644
--- a/drivers/cpufreq/scpi-cpufreq.c
+++ b/drivers/cpufreq/scpi-cpufreq.c
@@ -105,7 +105,6 @@ static int scpi_cpufreq_remove(struct platform_device *pdev)
static struct platform_driver scpi_cpufreq_platdrv = {
.driver = {
.name = "scpi-cpufreq",
- .owner = THIS_MODULE,
},
.probe = scpi_cpufreq_probe,
.remove = scpi_cpufreq_remove,
diff --git a/drivers/cpufreq/sti-cpufreq.c b/drivers/cpufreq/sti-cpufreq.c
index 0404203..b366e6d 100644
--- a/drivers/cpufreq/sti-cpufreq.c
+++ b/drivers/cpufreq/sti-cpufreq.c
@@ -163,7 +163,7 @@ static int sti_cpufreq_set_opp_info(void)
reg_fields = sti_cpufreq_match();
if (!reg_fields) {
- dev_err(dev, "This SoC doesn't support voltage scaling");
+ dev_err(dev, "This SoC doesn't support voltage scaling\n");
return -ENODEV;
}
diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c
index d5657d5..71e586d 100644
--- a/drivers/cpuidle/coupled.c
+++ b/drivers/cpuidle/coupled.c
@@ -749,65 +749,52 @@ static void cpuidle_coupled_allow_idle(struct cpuidle_coupled *coupled)
put_cpu();
}
-/**
- * cpuidle_coupled_cpu_notify - notifier called during hotplug transitions
- * @nb: notifier block
- * @action: hotplug transition
- * @hcpu: target cpu number
- *
- * Called when a cpu is brought on or offline using hotplug. Updates the
- * coupled cpu set appropriately
- */
-static int cpuidle_coupled_cpu_notify(struct notifier_block *nb,
- unsigned long action, void *hcpu)
+static int coupled_cpu_online(unsigned int cpu)
{
- int cpu = (unsigned long)hcpu;
struct cpuidle_device *dev;
- switch (action & ~CPU_TASKS_FROZEN) {
- case CPU_UP_PREPARE:
- case CPU_DOWN_PREPARE:
- case CPU_ONLINE:
- case CPU_DEAD:
- case CPU_UP_CANCELED:
- case CPU_DOWN_FAILED:
- break;
- default:
- return NOTIFY_OK;
- }
-
mutex_lock(&cpuidle_lock);
dev = per_cpu(cpuidle_devices, cpu);
- if (!dev || !dev->coupled)
- goto out;
-
- switch (action & ~CPU_TASKS_FROZEN) {
- case CPU_UP_PREPARE:
- case CPU_DOWN_PREPARE:
- cpuidle_coupled_prevent_idle(dev->coupled);
- break;
- case CPU_ONLINE:
- case CPU_DEAD:
+ if (dev && dev->coupled) {
cpuidle_coupled_update_online_cpus(dev->coupled);
- /* Fall through */
- case CPU_UP_CANCELED:
- case CPU_DOWN_FAILED:
cpuidle_coupled_allow_idle(dev->coupled);
- break;
}
-out:
mutex_unlock(&cpuidle_lock);
- return NOTIFY_OK;
+ return 0;
}
-static struct notifier_block cpuidle_coupled_cpu_notifier = {
- .notifier_call = cpuidle_coupled_cpu_notify,
-};
+static int coupled_cpu_up_prepare(unsigned int cpu)
+{
+ struct cpuidle_device *dev;
+
+ mutex_lock(&cpuidle_lock);
+
+ dev = per_cpu(cpuidle_devices, cpu);
+ if (dev && dev->coupled)
+ cpuidle_coupled_prevent_idle(dev->coupled);
+
+ mutex_unlock(&cpuidle_lock);
+ return 0;
+}
static int __init cpuidle_coupled_init(void)
{
- return register_cpu_notifier(&cpuidle_coupled_cpu_notifier);
+ int ret;
+
+ ret = cpuhp_setup_state_nocalls(CPUHP_CPUIDLE_COUPLED_PREPARE,
+ "cpuidle/coupled:prepare",
+ coupled_cpu_up_prepare,
+ coupled_cpu_online);
+ if (ret)
+ return ret;
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "cpuidle/coupled:online",
+ coupled_cpu_online,
+ coupled_cpu_up_prepare);
+ if (ret < 0)
+ cpuhp_remove_state_nocalls(CPUHP_CPUIDLE_COUPLED_PREPARE);
+ return ret;
}
core_initcall(cpuidle_coupled_init);
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index 4ba3d3f..f440d38 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -121,6 +121,7 @@ static int __init arm_idle_init(void)
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
pr_err("Failed to allocate cpuidle device\n");
+ ret = -ENOMEM;
goto out_fail;
}
dev->cpu = cpu;
diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c
index f7ca891..7fe442c 100644
--- a/drivers/cpuidle/cpuidle-powernv.c
+++ b/drivers/cpuidle/cpuidle-powernv.c
@@ -119,40 +119,30 @@ static struct cpuidle_state powernv_states[CPUIDLE_STATE_MAX] = {
.enter = snooze_loop },
};
-static int powernv_cpuidle_add_cpu_notifier(struct notifier_block *n,
- unsigned long action, void *hcpu)
+static int powernv_cpuidle_cpu_online(unsigned int cpu)
{
- int hotcpu = (unsigned long)hcpu;
- struct cpuidle_device *dev =
- per_cpu(cpuidle_devices, hotcpu);
+ struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
if (dev && cpuidle_get_driver()) {
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- cpuidle_pause_and_lock();
- cpuidle_enable_device(dev);
- cpuidle_resume_and_unlock();
- break;
+ cpuidle_pause_and_lock();
+ cpuidle_enable_device(dev);
+ cpuidle_resume_and_unlock();
+ }
+ return 0;
+}
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- cpuidle_pause_and_lock();
- cpuidle_disable_device(dev);
- cpuidle_resume_and_unlock();
- break;
+static int powernv_cpuidle_cpu_dead(unsigned int cpu)
+{
+ struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
- default:
- return NOTIFY_DONE;
- }
+ if (dev && cpuidle_get_driver()) {
+ cpuidle_pause_and_lock();
+ cpuidle_disable_device(dev);
+ cpuidle_resume_and_unlock();
}
- return NOTIFY_OK;
+ return 0;
}
-static struct notifier_block setup_hotplug_notifier = {
- .notifier_call = powernv_cpuidle_add_cpu_notifier,
-};
-
/*
* powernv_cpuidle_driver_init()
*/
@@ -355,7 +345,14 @@ static int __init powernv_processor_idle_init(void)
return retval;
}
- register_cpu_notifier(&setup_hotplug_notifier);
+ retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "cpuidle/powernv:online",
+ powernv_cpuidle_cpu_online, NULL);
+ WARN_ON(retval < 0);
+ retval = cpuhp_setup_state_nocalls(CPUHP_CPUIDLE_DEAD,
+ "cpuidle/powernv:dead", NULL,
+ powernv_cpuidle_cpu_dead);
+ WARN_ON(retval < 0);
printk(KERN_DEBUG "powernv_idle_driver registered\n");
return 0;
}
diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c
index 07135e0..166ccd7 100644
--- a/drivers/cpuidle/cpuidle-pseries.c
+++ b/drivers/cpuidle/cpuidle-pseries.c
@@ -171,40 +171,30 @@ static struct cpuidle_state shared_states[] = {
.enter = &shared_cede_loop },
};
-static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
- unsigned long action, void *hcpu)
+static int pseries_cpuidle_cpu_online(unsigned int cpu)
{
- int hotcpu = (unsigned long)hcpu;
- struct cpuidle_device *dev =
- per_cpu(cpuidle_devices, hotcpu);
+ struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
if (dev && cpuidle_get_driver()) {
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- cpuidle_pause_and_lock();
- cpuidle_enable_device(dev);
- cpuidle_resume_and_unlock();
- break;
+ cpuidle_pause_and_lock();
+ cpuidle_enable_device(dev);
+ cpuidle_resume_and_unlock();
+ }
+ return 0;
+}
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- cpuidle_pause_and_lock();
- cpuidle_disable_device(dev);
- cpuidle_resume_and_unlock();
- break;
+static int pseries_cpuidle_cpu_dead(unsigned int cpu)
+{
+ struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
- default:
- return NOTIFY_DONE;
- }
+ if (dev && cpuidle_get_driver()) {
+ cpuidle_pause_and_lock();
+ cpuidle_disable_device(dev);
+ cpuidle_resume_and_unlock();
}
- return NOTIFY_OK;
+ return 0;
}
-static struct notifier_block setup_hotplug_notifier = {
- .notifier_call = pseries_cpuidle_add_cpu_notifier,
-};
-
/*
* pseries_cpuidle_driver_init()
*/
@@ -273,7 +263,14 @@ static int __init pseries_processor_idle_init(void)
return retval;
}
- register_cpu_notifier(&setup_hotplug_notifier);
+ retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "cpuidle/pseries:online",
+ pseries_cpuidle_cpu_online, NULL);
+ WARN_ON(retval < 0);
+ retval = cpuhp_setup_state_nocalls(CPUHP_CPUIDLE_DEAD,
+ "cpuidle/pseries:DEAD", NULL,
+ pseries_cpuidle_cpu_dead);
+ WARN_ON(retval < 0);
printk(KERN_DEBUG "pseries_idle_driver registered\n");
return 0;
}
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index a5be56e..41254e7 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -76,7 +76,7 @@ comment "DEVFREQ Drivers"
config ARM_EXYNOS_BUS_DEVFREQ
tristate "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
- depends on ARCH_EXYNOS
+ depends on ARCH_EXYNOS || COMPILE_TEST
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select DEVFREQ_GOV_PASSIVE
select DEVFREQ_EVENT_EXYNOS_PPMU
@@ -91,14 +91,26 @@ config ARM_EXYNOS_BUS_DEVFREQ
This does not yet operate with optimal voltages.
config ARM_TEGRA_DEVFREQ
- tristate "Tegra DEVFREQ Driver"
- depends on ARCH_TEGRA_124_SOC
- select DEVFREQ_GOV_SIMPLE_ONDEMAND
- select PM_OPP
- help
- This adds the DEVFREQ driver for the Tegra family of SoCs.
- It reads ACTMON counters of memory controllers and adjusts the
- operating frequencies and voltages with OPP support.
+ tristate "Tegra DEVFREQ Driver"
+ depends on ARCH_TEGRA_124_SOC
+ select DEVFREQ_GOV_SIMPLE_ONDEMAND
+ select PM_OPP
+ help
+ This adds the DEVFREQ driver for the Tegra family of SoCs.
+ It reads ACTMON counters of memory controllers and adjusts the
+ operating frequencies and voltages with OPP support.
+
+config ARM_RK3399_DMC_DEVFREQ
+ tristate "ARM RK3399 DMC DEVFREQ Driver"
+ depends on ARCH_ROCKCHIP
+ select DEVFREQ_EVENT_ROCKCHIP_DFI
+ select DEVFREQ_GOV_SIMPLE_ONDEMAND
+ select PM_DEVFREQ_EVENT
+ select PM_OPP
+ help
+ This adds the DEVFREQ driver for the RK3399 DMC(Dynamic Memory Controller).
+ It sets the frequency for the memory controller and reads the usage counts
+ from hardware.
source "drivers/devfreq/event/Kconfig"
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 09f11d9..fbff40a 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
+obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o
# DEVFREQ Event Drivers
diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig
index eb6f74a..0fdae86 100644
--- a/drivers/devfreq/event/Kconfig
+++ b/drivers/devfreq/event/Kconfig
@@ -15,7 +15,7 @@ if PM_DEVFREQ_EVENT
config DEVFREQ_EVENT_EXYNOS_NOCP
tristate "EXYNOS NoC (Network On Chip) Probe DEVFREQ event Driver"
- depends on ARCH_EXYNOS
+ depends on ARCH_EXYNOS || COMPILE_TEST
select PM_OPP
help
This add the devfreq-event driver for Exynos SoC. It provides NoC
@@ -23,11 +23,18 @@ config DEVFREQ_EVENT_EXYNOS_NOCP
config DEVFREQ_EVENT_EXYNOS_PPMU
tristate "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver"
- depends on ARCH_EXYNOS
+ depends on ARCH_EXYNOS || COMPILE_TEST
select PM_OPP
help
This add the devfreq-event driver for Exynos SoC. It provides PPMU
(Platform Performance Monitoring Unit) counters to estimate the
utilization of each module.
+config DEVFREQ_EVENT_ROCKCHIP_DFI
+ tristate "ROCKCHIP DFI DEVFREQ event Driver"
+ depends on ARCH_ROCKCHIP
+ help
+ This add the devfreq-event driver for Rockchip SoC. It provides DFI
+ (DDR Monitor Module) driver to count ddr load.
+
endif # PM_DEVFREQ_EVENT
diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile
index 3d6afd3..dda7090 100644
--- a/drivers/devfreq/event/Makefile
+++ b/drivers/devfreq/event/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o
obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o
+obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o
diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c
index 845bf25..f55cf0e 100644
--- a/drivers/devfreq/event/exynos-ppmu.c
+++ b/drivers/devfreq/event/exynos-ppmu.c
@@ -406,8 +406,6 @@ static int of_get_devfreq_events(struct device_node *np,
of_property_read_string(node, "event-name", &desc[j].name);
j++;
-
- of_node_put(node);
}
info->desc = desc;
diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c
new file mode 100644
index 0000000..43fcc5a
--- /dev/null
+++ b/drivers/devfreq/event/rockchip-dfi.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/devfreq-event.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/of.h>
+
+#define RK3399_DMC_NUM_CH 2
+
+/* DDRMON_CTRL */
+#define DDRMON_CTRL 0x04
+#define CLR_DDRMON_CTRL (0x1f0000 << 0)
+#define LPDDR4_EN (0x10001 << 4)
+#define HARDWARE_EN (0x10001 << 3)
+#define LPDDR3_EN (0x10001 << 2)
+#define SOFTWARE_EN (0x10001 << 1)
+#define SOFTWARE_DIS (0x10000 << 1)
+#define TIME_CNT_EN (0x10001 << 0)
+
+#define DDRMON_CH0_COUNT_NUM 0x28
+#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c
+#define DDRMON_CH1_COUNT_NUM 0x3c
+#define DDRMON_CH1_DFI_ACCESS_NUM 0x40
+
+/* pmu grf */
+#define PMUGRF_OS_REG2 0x308
+#define DDRTYPE_SHIFT 13
+#define DDRTYPE_MASK 7
+
+enum {
+ DDR3 = 3,
+ LPDDR3 = 6,
+ LPDDR4 = 7,
+ UNUSED = 0xFF
+};
+
+struct dmc_usage {
+ u32 access;
+ u32 total;
+};
+
+/*
+ * The dfi controller can monitor DDR load. It has an upper and lower threshold
+ * for the operating points. Whenever the usage leaves these bounds an event is
+ * generated to indicate the DDR frequency should be changed.
+ */
+struct rockchip_dfi {
+ struct devfreq_event_dev *edev;
+ struct devfreq_event_desc *desc;
+ struct dmc_usage ch_usage[RK3399_DMC_NUM_CH];
+ struct device *dev;
+ void __iomem *regs;
+ struct regmap *regmap_pmu;
+ struct clk *clk;
+};
+
+static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
+{
+ struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+ void __iomem *dfi_regs = info->regs;
+ u32 val;
+ u32 ddr_type;
+
+ /* get ddr type */
+ regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, &val);
+ ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK;
+
+ /* clear DDRMON_CTRL setting */
+ writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL);
+
+ /* set ddr type to dfi */
+ if (ddr_type == LPDDR3)
+ writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL);
+ else if (ddr_type == LPDDR4)
+ writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL);
+
+ /* enable count, use software mode */
+ writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL);
+}
+
+static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
+{
+ struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+ void __iomem *dfi_regs = info->regs;
+
+ writel_relaxed(SOFTWARE_DIS, dfi_regs + DDRMON_CTRL);
+}
+
+static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev)
+{
+ struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+ u32 tmp, max = 0;
+ u32 i, busier_ch = 0;
+ void __iomem *dfi_regs = info->regs;
+
+ rockchip_dfi_stop_hardware_counter(edev);
+
+ /* Find out which channel is busier */
+ for (i = 0; i < RK3399_DMC_NUM_CH; i++) {
+ info->ch_usage[i].access = readl_relaxed(dfi_regs +
+ DDRMON_CH0_DFI_ACCESS_NUM + i * 20) * 4;
+ info->ch_usage[i].total = readl_relaxed(dfi_regs +
+ DDRMON_CH0_COUNT_NUM + i * 20);
+ tmp = info->ch_usage[i].access;
+ if (tmp > max) {
+ busier_ch = i;
+ max = tmp;
+ }
+ }
+ rockchip_dfi_start_hardware_counter(edev);
+
+ return busier_ch;
+}
+
+static int rockchip_dfi_disable(struct devfreq_event_dev *edev)
+{
+ struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+ rockchip_dfi_stop_hardware_counter(edev);
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+static int rockchip_dfi_enable(struct devfreq_event_dev *edev)
+{
+ struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+ int ret;
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret) {
+ dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret);
+ return ret;
+ }
+
+ rockchip_dfi_start_hardware_counter(edev);
+ return 0;
+}
+
+static int rockchip_dfi_set_event(struct devfreq_event_dev *edev)
+{
+ return 0;
+}
+
+static int rockchip_dfi_get_event(struct devfreq_event_dev *edev,
+ struct devfreq_event_data *edata)
+{
+ struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+ int busier_ch;
+
+ busier_ch = rockchip_dfi_get_busier_ch(edev);
+
+ edata->load_count = info->ch_usage[busier_ch].access;
+ edata->total_count = info->ch_usage[busier_ch].total;
+
+ return 0;
+}
+
+static const struct devfreq_event_ops rockchip_dfi_ops = {
+ .disable = rockchip_dfi_disable,
+ .enable = rockchip_dfi_enable,
+ .get_event = rockchip_dfi_get_event,
+ .set_event = rockchip_dfi_set_event,
+};
+
+static const struct of_device_id rockchip_dfi_id_match[] = {
+ { .compatible = "rockchip,rk3399-dfi" },
+ { },
+};
+
+static int rockchip_dfi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rockchip_dfi *data;
+ struct resource *res;
+ struct devfreq_event_desc *desc;
+ struct device_node *np = pdev->dev.of_node, *node;
+
+ data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->regs))
+ return PTR_ERR(data->regs);
+
+ data->clk = devm_clk_get(dev, "pclk_ddr_mon");
+ if (IS_ERR(data->clk)) {
+ dev_err(dev, "Cannot get the clk dmc_clk\n");
+ return PTR_ERR(data->clk);
+ };
+
+ /* try to find the optional reference to the pmu syscon */
+ node = of_parse_phandle(np, "rockchip,pmu", 0);
+ if (node) {
+ data->regmap_pmu = syscon_node_to_regmap(node);
+ if (IS_ERR(data->regmap_pmu))
+ return PTR_ERR(data->regmap_pmu);
+ }
+ data->dev = dev;
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->ops = &rockchip_dfi_ops;
+ desc->driver_data = data;
+ desc->name = np->name;
+ data->desc = desc;
+
+ data->edev = devm_devfreq_event_add_edev(&pdev->dev, desc);
+ if (IS_ERR(data->edev)) {
+ dev_err(&pdev->dev,
+ "failed to add devfreq-event device\n");
+ return PTR_ERR(data->edev);
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static struct platform_driver rockchip_dfi_driver = {
+ .probe = rockchip_dfi_probe,
+ .driver = {
+ .name = "rockchip-dfi",
+ .of_match_table = rockchip_dfi_id_match,
+ },
+};
+module_platform_driver(rockchip_dfi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip DFI driver");
diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c
new file mode 100644
index 0000000..e24b73d
--- /dev/null
+++ b/drivers/devfreq/rk3399_dmc.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd.
+ * Author: Lin Huang <hl@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
+#include <linux/rwsem.h>
+#include <linux/suspend.h>
+
+#include <soc/rockchip/rockchip_sip.h>
+
+struct dram_timing {
+ unsigned int ddr3_speed_bin;
+ unsigned int pd_idle;
+ unsigned int sr_idle;
+ unsigned int sr_mc_gate_idle;
+ unsigned int srpd_lite_idle;
+ unsigned int standby_idle;
+ unsigned int auto_pd_dis_freq;
+ unsigned int dram_dll_dis_freq;
+ unsigned int phy_dll_dis_freq;
+ unsigned int ddr3_odt_dis_freq;
+ unsigned int ddr3_drv;
+ unsigned int ddr3_odt;
+ unsigned int phy_ddr3_ca_drv;
+ unsigned int phy_ddr3_dq_drv;
+ unsigned int phy_ddr3_odt;
+ unsigned int lpddr3_odt_dis_freq;
+ unsigned int lpddr3_drv;
+ unsigned int lpddr3_odt;
+ unsigned int phy_lpddr3_ca_drv;
+ unsigned int phy_lpddr3_dq_drv;
+ unsigned int phy_lpddr3_odt;
+ unsigned int lpddr4_odt_dis_freq;
+ unsigned int lpddr4_drv;
+ unsigned int lpddr4_dq_odt;
+ unsigned int lpddr4_ca_odt;
+ unsigned int phy_lpddr4_ca_drv;
+ unsigned int phy_lpddr4_ck_cs_drv;
+ unsigned int phy_lpddr4_dq_drv;
+ unsigned int phy_lpddr4_odt;
+};
+
+struct rk3399_dmcfreq {
+ struct device *dev;
+ struct devfreq *devfreq;
+ struct devfreq_simple_ondemand_data ondemand_data;
+ struct clk *dmc_clk;
+ struct devfreq_event_dev *edev;
+ struct mutex lock;
+ struct dram_timing timing;
+
+ /*
+ * DDR Converser of Frequency (DCF) is used to implement DDR frequency
+ * conversion without the participation of CPU, we will implement and
+ * control it in arm trust firmware.
+ */
+ wait_queue_head_t wait_dcf_queue;
+ int irq;
+ int wait_dcf_flag;
+ struct regulator *vdd_center;
+ unsigned long rate, target_rate;
+ unsigned long volt, target_volt;
+ struct dev_pm_opp *curr_opp;
+};
+
+static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
+ u32 flags)
+{
+ struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
+ struct dev_pm_opp *opp;
+ unsigned long old_clk_rate = dmcfreq->rate;
+ unsigned long target_volt, target_rate;
+ int err;
+
+ rcu_read_lock();
+ opp = devfreq_recommended_opp(dev, freq, flags);
+ if (IS_ERR(opp)) {
+ rcu_read_unlock();
+ return PTR_ERR(opp);
+ }
+
+ target_rate = dev_pm_opp_get_freq(opp);
+ target_volt = dev_pm_opp_get_voltage(opp);
+
+ dmcfreq->rate = dev_pm_opp_get_freq(dmcfreq->curr_opp);
+ dmcfreq->volt = dev_pm_opp_get_voltage(dmcfreq->curr_opp);
+
+ rcu_read_unlock();
+
+ if (dmcfreq->rate == target_rate)
+ return 0;
+
+ mutex_lock(&dmcfreq->lock);
+
+ /*
+ * If frequency scaling from low to high, adjust voltage first.
+ * If frequency scaling from high to low, adjust frequency first.
+ */
+ if (old_clk_rate < target_rate) {
+ err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
+ target_volt);
+ if (err) {
+ dev_err(dev, "Cannot to set voltage %lu uV\n",
+ target_volt);
+ goto out;
+ }
+ }
+ dmcfreq->wait_dcf_flag = 1;
+
+ err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
+ if (err) {
+ dev_err(dev, "Cannot to set frequency %lu (%d)\n",
+ target_rate, err);
+ regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
+ dmcfreq->volt);
+ goto out;
+ }
+
+ /*
+ * Wait until bcf irq happen, it means freq scaling finish in
+ * arm trust firmware, use 100ms as timeout time.
+ */
+ if (!wait_event_timeout(dmcfreq->wait_dcf_queue,
+ !dmcfreq->wait_dcf_flag, HZ / 10))
+ dev_warn(dev, "Timeout waiting for dcf interrupt\n");
+
+ /*
+ * Check the dpll rate,
+ * There only two result we will get,
+ * 1. Ddr frequency scaling fail, we still get the old rate.
+ * 2. Ddr frequency scaling sucessful, we get the rate we set.
+ */
+ dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
+
+ /* If get the incorrect rate, set voltage to old value. */
+ if (dmcfreq->rate != target_rate) {
+ dev_err(dev, "Get wrong ddr frequency, Request frequency %lu,\
+ Current frequency %lu\n", target_rate, dmcfreq->rate);
+ regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
+ dmcfreq->volt);
+ goto out;
+ } else if (old_clk_rate > target_rate)
+ err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
+ target_volt);
+ if (err)
+ dev_err(dev, "Cannot to set vol %lu uV\n", target_volt);
+
+ dmcfreq->curr_opp = opp;
+out:
+ mutex_unlock(&dmcfreq->lock);
+ return err;
+}
+
+static int rk3399_dmcfreq_get_dev_status(struct device *dev,
+ struct devfreq_dev_status *stat)
+{
+ struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
+ struct devfreq_event_data edata;
+ int ret = 0;
+
+ ret = devfreq_event_get_event(dmcfreq->edev, &edata);
+ if (ret < 0)
+ return ret;
+
+ stat->current_frequency = dmcfreq->rate;
+ stat->busy_time = edata.load_count;
+ stat->total_time = edata.total_count;
+
+ return ret;
+}
+
+static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+ struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
+
+ *freq = dmcfreq->rate;
+
+ return 0;
+}
+
+static struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
+ .polling_ms = 200,
+ .target = rk3399_dmcfreq_target,
+ .get_dev_status = rk3399_dmcfreq_get_dev_status,
+ .get_cur_freq = rk3399_dmcfreq_get_cur_freq,
+};
+
+static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
+{
+ struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = devfreq_event_disable_edev(dmcfreq->edev);
+ if (ret < 0) {
+ dev_err(dev, "failed to disable the devfreq-event devices\n");
+ return ret;
+ }
+
+ ret = devfreq_suspend_device(dmcfreq->devfreq);
+ if (ret < 0) {
+ dev_err(dev, "failed to suspend the devfreq devices\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
+{
+ struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = devfreq_event_enable_edev(dmcfreq->edev);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable the devfreq-event devices\n");
+ return ret;
+ }
+
+ ret = devfreq_resume_device(dmcfreq->devfreq);
+ if (ret < 0) {
+ dev_err(dev, "failed to resume the devfreq devices\n");
+ return ret;
+ }
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
+ rk3399_dmcfreq_resume);
+
+static irqreturn_t rk3399_dmc_irq(int irq, void *dev_id)
+{
+ struct rk3399_dmcfreq *dmcfreq = dev_id;
+ struct arm_smccc_res res;
+
+ dmcfreq->wait_dcf_flag = 0;
+ wake_up(&dmcfreq->wait_dcf_queue);
+
+ /* Clear the DCF interrupt */
+ arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
+ ROCKCHIP_SIP_CONFIG_DRAM_CLR_IRQ,
+ 0, 0, 0, 0, &res);
+
+ return IRQ_HANDLED;
+}
+
+static int of_get_ddr_timings(struct dram_timing *timing,
+ struct device_node *np)
+{
+ int ret = 0;
+
+ ret = of_property_read_u32(np, "rockchip,ddr3_speed_bin",
+ &timing->ddr3_speed_bin);
+ ret |= of_property_read_u32(np, "rockchip,pd_idle",
+ &timing->pd_idle);
+ ret |= of_property_read_u32(np, "rockchip,sr_idle",
+ &timing->sr_idle);
+ ret |= of_property_read_u32(np, "rockchip,sr_mc_gate_idle",
+ &timing->sr_mc_gate_idle);
+ ret |= of_property_read_u32(np, "rockchip,srpd_lite_idle",
+ &timing->srpd_lite_idle);
+ ret |= of_property_read_u32(np, "rockchip,standby_idle",
+ &timing->standby_idle);
+ ret |= of_property_read_u32(np, "rockchip,auto_pd_dis_freq",
+ &timing->auto_pd_dis_freq);
+ ret |= of_property_read_u32(np, "rockchip,dram_dll_dis_freq",
+ &timing->dram_dll_dis_freq);
+ ret |= of_property_read_u32(np, "rockchip,phy_dll_dis_freq",
+ &timing->phy_dll_dis_freq);
+ ret |= of_property_read_u32(np, "rockchip,ddr3_odt_dis_freq",
+ &timing->ddr3_odt_dis_freq);
+ ret |= of_property_read_u32(np, "rockchip,ddr3_drv",
+ &timing->ddr3_drv);
+ ret |= of_property_read_u32(np, "rockchip,ddr3_odt",
+ &timing->ddr3_odt);
+ ret |= of_property_read_u32(np, "rockchip,phy_ddr3_ca_drv",
+ &timing->phy_ddr3_ca_drv);
+ ret |= of_property_read_u32(np, "rockchip,phy_ddr3_dq_drv",
+ &timing->phy_ddr3_dq_drv);
+ ret |= of_property_read_u32(np, "rockchip,phy_ddr3_odt",
+ &timing->phy_ddr3_odt);
+ ret |= of_property_read_u32(np, "rockchip,lpddr3_odt_dis_freq",
+ &timing->lpddr3_odt_dis_freq);
+ ret |= of_property_read_u32(np, "rockchip,lpddr3_drv",
+ &timing->lpddr3_drv);
+ ret |= of_property_read_u32(np, "rockchip,lpddr3_odt",
+ &timing->lpddr3_odt);
+ ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_ca_drv",
+ &timing->phy_lpddr3_ca_drv);
+ ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_dq_drv",
+ &timing->phy_lpddr3_dq_drv);
+ ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_odt",
+ &timing->phy_lpddr3_odt);
+ ret |= of_property_read_u32(np, "rockchip,lpddr4_odt_dis_freq",
+ &timing->lpddr4_odt_dis_freq);
+ ret |= of_property_read_u32(np, "rockchip,lpddr4_drv",
+ &timing->lpddr4_drv);
+ ret |= of_property_read_u32(np, "rockchip,lpddr4_dq_odt",
+ &timing->lpddr4_dq_odt);
+ ret |= of_property_read_u32(np, "rockchip,lpddr4_ca_odt",
+ &timing->lpddr4_ca_odt);
+ ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_ca_drv",
+ &timing->phy_lpddr4_ca_drv);
+ ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_ck_cs_drv",
+ &timing->phy_lpddr4_ck_cs_drv);
+ ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_dq_drv",
+ &timing->phy_lpddr4_dq_drv);
+ ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_odt",
+ &timing->phy_lpddr4_odt);
+
+ return ret;
+}
+
+static int rk3399_dmcfreq_probe(struct platform_device *pdev)
+{
+ struct arm_smccc_res res;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct rk3399_dmcfreq *data;
+ int ret, irq, index, size;
+ uint32_t *timing;
+ struct dev_pm_opp *opp;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Cannot get the dmc interrupt resource\n");
+ return -EINVAL;
+ }
+ data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->lock);
+
+ data->vdd_center = devm_regulator_get(dev, "center");
+ if (IS_ERR(data->vdd_center)) {
+ dev_err(dev, "Cannot get the regulator \"center\"\n");
+ return PTR_ERR(data->vdd_center);
+ }
+
+ data->dmc_clk = devm_clk_get(dev, "dmc_clk");
+ if (IS_ERR(data->dmc_clk)) {
+ dev_err(dev, "Cannot get the clk dmc_clk\n");
+ return PTR_ERR(data->dmc_clk);
+ };
+
+ data->irq = irq;
+ ret = devm_request_irq(dev, irq, rk3399_dmc_irq, 0,
+ dev_name(dev), data);
+ if (ret) {
+ dev_err(dev, "Failed to request dmc irq: %d\n", ret);
+ return ret;
+ }
+
+ init_waitqueue_head(&data->wait_dcf_queue);
+ data->wait_dcf_flag = 0;
+
+ data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(data->edev))
+ return -EPROBE_DEFER;
+
+ ret = devfreq_event_enable_edev(data->edev);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable devfreq-event devices\n");
+ return ret;
+ }
+
+ /*
+ * Get dram timing and pass it to arm trust firmware,
+ * the dram drvier in arm trust firmware will get these
+ * timing and to do dram initial.
+ */
+ if (!of_get_ddr_timings(&data->timing, np)) {
+ timing = &data->timing.ddr3_speed_bin;
+ size = sizeof(struct dram_timing) / 4;
+ for (index = 0; index < size; index++) {
+ arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, *timing++, index,
+ ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM,
+ 0, 0, 0, 0, &res);
+ if (res.a0) {
+ dev_err(dev, "Failed to set dram param: %ld\n",
+ res.a0);
+ return -EINVAL;
+ }
+ }
+ }
+
+ arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
+ ROCKCHIP_SIP_CONFIG_DRAM_INIT,
+ 0, 0, 0, 0, &res);
+
+ /*
+ * We add a devfreq driver to our parent since it has a device tree node
+ * with operating points.
+ */
+ if (dev_pm_opp_of_add_table(dev)) {
+ dev_err(dev, "Invalid operating-points in device tree.\n");
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ of_property_read_u32(np, "upthreshold",
+ &data->ondemand_data.upthreshold);
+ of_property_read_u32(np, "downdifferential",
+ &data->ondemand_data.downdifferential);
+
+ data->rate = clk_get_rate(data->dmc_clk);
+
+ rcu_read_lock();
+ opp = devfreq_recommended_opp(dev, &data->rate, 0);
+ if (IS_ERR(opp)) {
+ rcu_read_unlock();
+ return PTR_ERR(opp);
+ }
+ rcu_read_unlock();
+ data->curr_opp = opp;
+
+ rk3399_devfreq_dmc_profile.initial_freq = data->rate;
+
+ data->devfreq = devfreq_add_device(dev,
+ &rk3399_devfreq_dmc_profile,
+ "simple_ondemand",
+ &data->ondemand_data);
+ if (IS_ERR(data->devfreq))
+ return PTR_ERR(data->devfreq);
+ devm_devfreq_register_opp_notifier(dev, data->devfreq);
+
+ data->dev = dev;
+ platform_set_drvdata(pdev, data);
+
+ return 0;
+}
+
+static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
+ { .compatible = "rockchip,rk3399-dmc" },
+ { },
+};
+
+static struct platform_driver rk3399_dmcfreq_driver = {
+ .probe = rk3399_dmcfreq_probe,
+ .driver = {
+ .name = "rk3399-dmc-freq",
+ .pm = &rk3399_dmcfreq_pm,
+ .of_match_table = rk3399dmc_devfreq_of_match,
+ },
+};
+module_platform_driver(rk3399_dmcfreq_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
+MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index edf053f..da18b18 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -46,9 +46,9 @@
u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \
DW_DMA_MSIZE_16; \
u8 _dms = (_dwc->direction == DMA_MEM_TO_DEV) ? \
- _dwc->p_master : _dwc->m_master; \
+ _dwc->dws.p_master : _dwc->dws.m_master; \
u8 _sms = (_dwc->direction == DMA_DEV_TO_MEM) ? \
- _dwc->p_master : _dwc->m_master; \
+ _dwc->dws.p_master : _dwc->dws.m_master; \
\
(DWC_CTLL_DST_MSIZE(_dmsize) \
| DWC_CTLL_SRC_MSIZE(_smsize) \
@@ -143,12 +143,16 @@ static void dwc_initialize(struct dw_dma_chan *dwc)
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
u32 cfghi = DWC_CFGH_FIFO_MODE;
u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
+ bool hs_polarity = dwc->dws.hs_polarity;
if (test_bit(DW_DMA_IS_INITIALIZED, &dwc->flags))
return;
- cfghi |= DWC_CFGH_DST_PER(dwc->dst_id);
- cfghi |= DWC_CFGH_SRC_PER(dwc->src_id);
+ cfghi |= DWC_CFGH_DST_PER(dwc->dws.dst_id);
+ cfghi |= DWC_CFGH_SRC_PER(dwc->dws.src_id);
+
+ /* Set polarity of handshake interface */
+ cfglo |= hs_polarity ? DWC_CFGL_HS_DST_POL | DWC_CFGL_HS_SRC_POL : 0;
channel_writel(dwc, CFG_LO, cfglo);
channel_writel(dwc, CFG_HI, cfghi);
@@ -209,7 +213,7 @@ static inline void dwc_do_single_block(struct dw_dma_chan *dwc,
static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first)
{
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
- u8 lms = DWC_LLP_LMS(dwc->m_master);
+ u8 lms = DWC_LLP_LMS(dwc->dws.m_master);
unsigned long was_soft_llp;
/* ASSERT: channel is idle */
@@ -662,7 +666,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
struct dw_desc *prev;
size_t xfer_count;
size_t offset;
- u8 m_master = dwc->m_master;
+ u8 m_master = dwc->dws.m_master;
unsigned int src_width;
unsigned int dst_width;
unsigned int data_width = dw->pdata->data_width[m_master];
@@ -740,7 +744,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
struct dw_desc *prev;
struct dw_desc *first;
u32 ctllo;
- u8 m_master = dwc->m_master;
+ u8 m_master = dwc->dws.m_master;
u8 lms = DWC_LLP_LMS(m_master);
dma_addr_t reg;
unsigned int reg_width;
@@ -895,12 +899,7 @@ bool dw_dma_filter(struct dma_chan *chan, void *param)
return false;
/* We have to copy data since dws can be temporary storage */
-
- dwc->src_id = dws->src_id;
- dwc->dst_id = dws->dst_id;
-
- dwc->m_master = dws->m_master;
- dwc->p_master = dws->p_master;
+ memcpy(&dwc->dws, dws, sizeof(struct dw_dma_slave));
return true;
}
@@ -1167,11 +1166,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan)
spin_lock_irqsave(&dwc->lock, flags);
/* Clear custom channel configuration */
- dwc->src_id = 0;
- dwc->dst_id = 0;
-
- dwc->m_master = 0;
- dwc->p_master = 0;
+ memset(&dwc->dws, 0, sizeof(struct dw_dma_slave));
clear_bit(DW_DMA_IS_INITIALIZED, &dwc->flags);
@@ -1264,7 +1259,7 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
struct dw_cyclic_desc *retval = NULL;
struct dw_desc *desc;
struct dw_desc *last = NULL;
- u8 lms = DWC_LLP_LMS(dwc->m_master);
+ u8 lms = DWC_LLP_LMS(dwc->dws.m_master);
unsigned long was_cyclic;
unsigned int reg_width;
unsigned int periods;
@@ -1576,11 +1571,7 @@ int dw_dma_probe(struct dw_dma_chip *chip)
(dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0;
} else {
dwc->block_size = pdata->block_size;
-
- /* Check if channel supports multi block transfer */
- channel_writel(dwc, LLP, DWC_LLP_LOC(0xffffffff));
- dwc->nollp = DWC_LLP_LOC(channel_readl(dwc, LLP)) == 0;
- channel_writel(dwc, LLP, 0);
+ dwc->nollp = pdata->is_nollp;
}
}
diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h
index 4b7bd78..f65dd10 100644
--- a/drivers/dma/dw/regs.h
+++ b/drivers/dma/dw/regs.h
@@ -245,10 +245,7 @@ struct dw_dma_chan {
bool nollp;
/* custom slave configuration */
- u8 src_id;
- u8 dst_id;
- u8 m_master;
- u8 p_master;
+ struct dw_dma_slave dws;
/* configuration passed via .device_config */
struct dma_slave_config dma_sconfig;
diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c
index c5f21ef..29d04ca 100644
--- a/drivers/dma/hsu/hsu.c
+++ b/drivers/dma/hsu/hsu.c
@@ -200,10 +200,9 @@ EXPORT_SYMBOL_GPL(hsu_dma_get_status);
* is not a normal timeout interrupt, ie. hsu_dma_get_status() returned 0.
*
* Return:
- * IRQ_NONE for invalid channel number, IRQ_HANDLED otherwise.
+ * 0 for invalid channel number, 1 otherwise.
*/
-irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr,
- u32 status)
+int hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr, u32 status)
{
struct hsu_dma_chan *hsuc;
struct hsu_dma_desc *desc;
@@ -211,7 +210,7 @@ irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr,
/* Sanity check */
if (nr >= chip->hsu->nr_channels)
- return IRQ_NONE;
+ return 0;
hsuc = &chip->hsu->chan[nr];
@@ -230,7 +229,7 @@ irqreturn_t hsu_dma_do_irq(struct hsu_dma_chip *chip, unsigned short nr,
}
spin_unlock_irqrestore(&hsuc->vchan.lock, flags);
- return IRQ_HANDLED;
+ return 1;
}
EXPORT_SYMBOL_GPL(hsu_dma_do_irq);
diff --git a/drivers/dma/hsu/pci.c b/drivers/dma/hsu/pci.c
index 9916058..b51639f 100644
--- a/drivers/dma/hsu/pci.c
+++ b/drivers/dma/hsu/pci.c
@@ -29,7 +29,7 @@ static irqreturn_t hsu_pci_irq(int irq, void *dev)
u32 dmaisr;
u32 status;
unsigned short i;
- irqreturn_t ret = IRQ_NONE;
+ int ret = 0;
int err;
dmaisr = readl(chip->regs + HSU_PCI_DMAISR);
@@ -37,14 +37,14 @@ static irqreturn_t hsu_pci_irq(int irq, void *dev)
if (dmaisr & 0x1) {
err = hsu_dma_get_status(chip, i, &status);
if (err > 0)
- ret |= IRQ_HANDLED;
+ ret |= 1;
else if (err == 0)
ret |= hsu_dma_do_irq(chip, i, status);
}
dmaisr >>= 1;
}
- return ret;
+ return IRQ_RETVAL(ret);
}
static int hsu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 03ec76f..3cb4738 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -648,15 +648,11 @@ static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
writel_relaxed(val, sdma->regs + chnenbl);
}
-static void sdma_handle_channel_loop(struct sdma_channel *sdmac)
-{
- if (sdmac->desc.callback)
- sdmac->desc.callback(sdmac->desc.callback_param);
-}
-
static void sdma_update_channel_loop(struct sdma_channel *sdmac)
{
struct sdma_buffer_descriptor *bd;
+ int error = 0;
+ enum dma_status old_status = sdmac->status;
/*
* loop mode. Iterate over descriptors, re-setup them and
@@ -668,17 +664,42 @@ static void sdma_update_channel_loop(struct sdma_channel *sdmac)
if (bd->mode.status & BD_DONE)
break;
- if (bd->mode.status & BD_RROR)
+ if (bd->mode.status & BD_RROR) {
+ bd->mode.status &= ~BD_RROR;
sdmac->status = DMA_ERROR;
+ error = -EIO;
+ }
+
+ /*
+ * We use bd->mode.count to calculate the residue, since contains
+ * the number of bytes present in the current buffer descriptor.
+ */
+ sdmac->chn_real_count = bd->mode.count;
bd->mode.status |= BD_DONE;
+ bd->mode.count = sdmac->period_len;
+
+ /*
+ * The callback is called from the interrupt context in order
+ * to reduce latency and to avoid the risk of altering the
+ * SDMA transaction status by the time the client tasklet is
+ * executed.
+ */
+
+ if (sdmac->desc.callback)
+ sdmac->desc.callback(sdmac->desc.callback_param);
+
sdmac->buf_tail++;
sdmac->buf_tail %= sdmac->num_bd;
+
+ if (error)
+ sdmac->status = old_status;
}
}
-static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac)
+static void mxc_sdma_handle_channel_normal(unsigned long data)
{
+ struct sdma_channel *sdmac = (struct sdma_channel *) data;
struct sdma_buffer_descriptor *bd;
int i, error = 0;
@@ -705,16 +726,6 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac)
sdmac->desc.callback(sdmac->desc.callback_param);
}
-static void sdma_tasklet(unsigned long data)
-{
- struct sdma_channel *sdmac = (struct sdma_channel *) data;
-
- if (sdmac->flags & IMX_DMA_SG_LOOP)
- sdma_handle_channel_loop(sdmac);
- else
- mxc_sdma_handle_channel_normal(sdmac);
-}
-
static irqreturn_t sdma_int_handler(int irq, void *dev_id)
{
struct sdma_engine *sdma = dev_id;
@@ -731,8 +742,8 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
if (sdmac->flags & IMX_DMA_SG_LOOP)
sdma_update_channel_loop(sdmac);
-
- tasklet_schedule(&sdmac->tasklet);
+ else
+ tasklet_schedule(&sdmac->tasklet);
__clear_bit(channel, &stat);
}
@@ -1353,7 +1364,8 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan,
u32 residue;
if (sdmac->flags & IMX_DMA_SG_LOOP)
- residue = (sdmac->num_bd - sdmac->buf_tail) * sdmac->period_len;
+ residue = (sdmac->num_bd - sdmac->buf_tail) *
+ sdmac->period_len - sdmac->chn_real_count;
else
residue = sdmac->chn_count - sdmac->chn_real_count;
@@ -1732,7 +1744,7 @@ static int sdma_probe(struct platform_device *pdev)
dma_cookie_init(&sdmac->chan);
sdmac->channel = i;
- tasklet_init(&sdmac->tasklet, sdma_tasklet,
+ tasklet_init(&sdmac->tasklet, mxc_sdma_handle_channel_normal,
(unsigned long) sdmac);
/*
* Add the channel to the DMAC list. Do not add channel 0 though
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index 9b6800a..daaac2c 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -148,12 +148,12 @@ static const char * const mc6_mce_desc[] = {
};
/* Scalable MCA error strings */
-static const char * const f17h_ls_mce_desc[] = {
+static const char * const smca_ls_mce_desc[] = {
"Load queue parity",
"Store queue parity",
"Miss address buffer payload parity",
"L1 TLB parity",
- "", /* reserved */
+ "Reserved",
"DC tag error type 6",
"DC tag error type 1",
"Internal error type 1",
@@ -172,7 +172,7 @@ static const char * const f17h_ls_mce_desc[] = {
"L2 fill data error",
};
-static const char * const f17h_if_mce_desc[] = {
+static const char * const smca_if_mce_desc[] = {
"microtag probe port parity error",
"IC microtag or full tag multi-hit error",
"IC full tag parity",
@@ -185,19 +185,22 @@ static const char * const f17h_if_mce_desc[] = {
"BPQ snoop parity on Thread 1",
"L1 BTB multi-match error",
"L2 BTB multi-match error",
+ "L2 Cache Response Poison error",
+ "System Read Data error",
};
-static const char * const f17h_l2_mce_desc[] = {
+static const char * const smca_l2_mce_desc[] = {
"L2M tag multi-way-hit error",
"L2M tag ECC error",
"L2M data ECC error",
"HW assert",
};
-static const char * const f17h_de_mce_desc[] = {
+static const char * const smca_de_mce_desc[] = {
"uop cache tag parity error",
"uop cache data parity error",
"Insn buffer parity error",
+ "uop queue parity error",
"Insn dispatch queue parity error",
"Fetch address FIFO parity",
"Patch RAM data parity",
@@ -205,7 +208,7 @@ static const char * const f17h_de_mce_desc[] = {
"uop buffer parity"
};
-static const char * const f17h_ex_mce_desc[] = {
+static const char * const smca_ex_mce_desc[] = {
"Watchdog timeout error",
"Phy register file parity",
"Flag register file parity",
@@ -214,18 +217,22 @@ static const char * const f17h_ex_mce_desc[] = {
"EX payload parity",
"Checkpoint queue parity",
"Retire dispatch queue parity",
+ "Retire status queue parity error",
+ "Scheduling queue parity error",
+ "Branch buffer queue parity error",
};
-static const char * const f17h_fp_mce_desc[] = {
+static const char * const smca_fp_mce_desc[] = {
"Physical register file parity",
"Freelist parity error",
"Schedule queue parity",
"NSQ parity error",
"Retire queue parity",
"Status register file parity",
+ "Hardware assertion",
};
-static const char * const f17h_l3_mce_desc[] = {
+static const char * const smca_l3_mce_desc[] = {
"Shadow tag macro ECC error",
"Shadow tag macro multi-way-hit error",
"L3M tag ECC error",
@@ -236,7 +243,7 @@ static const char * const f17h_l3_mce_desc[] = {
"L3 HW assert",
};
-static const char * const f17h_cs_mce_desc[] = {
+static const char * const smca_cs_mce_desc[] = {
"Illegal request from transport layer",
"Address violation",
"Security violation",
@@ -248,14 +255,14 @@ static const char * const f17h_cs_mce_desc[] = {
"ECC error on probe filter access",
};
-static const char * const f17h_pie_mce_desc[] = {
+static const char * const smca_pie_mce_desc[] = {
"HW assert",
"Internal PIE register security violation",
"Error on GMI link",
"Poison data written to internal PIE register",
};
-static const char * const f17h_umc_mce_desc[] = {
+static const char * const smca_umc_mce_desc[] = {
"DRAM ECC error",
"Data poison error on DRAM",
"SDP parity error",
@@ -264,18 +271,39 @@ static const char * const f17h_umc_mce_desc[] = {
"Write data CRC error",
};
-static const char * const f17h_pb_mce_desc[] = {
+static const char * const smca_pb_mce_desc[] = {
"Parameter Block RAM ECC error",
};
-static const char * const f17h_psp_mce_desc[] = {
+static const char * const smca_psp_mce_desc[] = {
"PSP RAM ECC or parity error",
};
-static const char * const f17h_smu_mce_desc[] = {
+static const char * const smca_smu_mce_desc[] = {
"SMU RAM ECC or parity error",
};
+struct smca_mce_desc {
+ const char * const *descs;
+ unsigned int num_descs;
+};
+
+static struct smca_mce_desc smca_mce_descs[] = {
+ [SMCA_LS] = { smca_ls_mce_desc, ARRAY_SIZE(smca_ls_mce_desc) },
+ [SMCA_IF] = { smca_if_mce_desc, ARRAY_SIZE(smca_if_mce_desc) },
+ [SMCA_L2_CACHE] = { smca_l2_mce_desc, ARRAY_SIZE(smca_l2_mce_desc) },
+ [SMCA_DE] = { smca_de_mce_desc, ARRAY_SIZE(smca_de_mce_desc) },
+ [SMCA_EX] = { smca_ex_mce_desc, ARRAY_SIZE(smca_ex_mce_desc) },
+ [SMCA_FP] = { smca_fp_mce_desc, ARRAY_SIZE(smca_fp_mce_desc) },
+ [SMCA_L3_CACHE] = { smca_l3_mce_desc, ARRAY_SIZE(smca_l3_mce_desc) },
+ [SMCA_CS] = { smca_cs_mce_desc, ARRAY_SIZE(smca_cs_mce_desc) },
+ [SMCA_PIE] = { smca_pie_mce_desc, ARRAY_SIZE(smca_pie_mce_desc) },
+ [SMCA_UMC] = { smca_umc_mce_desc, ARRAY_SIZE(smca_umc_mce_desc) },
+ [SMCA_PB] = { smca_pb_mce_desc, ARRAY_SIZE(smca_pb_mce_desc) },
+ [SMCA_PSP] = { smca_psp_mce_desc, ARRAY_SIZE(smca_psp_mce_desc) },
+ [SMCA_SMU] = { smca_smu_mce_desc, ARRAY_SIZE(smca_smu_mce_desc) },
+};
+
static bool f12h_mc0_mce(u16 ec, u8 xec)
{
bool ret = false;
@@ -820,175 +848,35 @@ static void decode_mc6_mce(struct mce *m)
pr_emerg(HW_ERR "Corrupted MC6 MCE info?\n");
}
-static void decode_f17h_core_errors(const char *ip_name, u8 xec,
- unsigned int mca_type)
-{
- const char * const *error_desc_array;
- size_t len;
-
- pr_emerg(HW_ERR "%s Error: ", ip_name);
-
- switch (mca_type) {
- case SMCA_LS:
- error_desc_array = f17h_ls_mce_desc;
- len = ARRAY_SIZE(f17h_ls_mce_desc) - 1;
-
- if (xec == 0x4) {
- pr_cont("Unrecognized LS MCA error code.\n");
- return;
- }
- break;
-
- case SMCA_IF:
- error_desc_array = f17h_if_mce_desc;
- len = ARRAY_SIZE(f17h_if_mce_desc) - 1;
- break;
-
- case SMCA_L2_CACHE:
- error_desc_array = f17h_l2_mce_desc;
- len = ARRAY_SIZE(f17h_l2_mce_desc) - 1;
- break;
-
- case SMCA_DE:
- error_desc_array = f17h_de_mce_desc;
- len = ARRAY_SIZE(f17h_de_mce_desc) - 1;
- break;
-
- case SMCA_EX:
- error_desc_array = f17h_ex_mce_desc;
- len = ARRAY_SIZE(f17h_ex_mce_desc) - 1;
- break;
-
- case SMCA_FP:
- error_desc_array = f17h_fp_mce_desc;
- len = ARRAY_SIZE(f17h_fp_mce_desc) - 1;
- break;
-
- case SMCA_L3_CACHE:
- error_desc_array = f17h_l3_mce_desc;
- len = ARRAY_SIZE(f17h_l3_mce_desc) - 1;
- break;
-
- default:
- pr_cont("Corrupted MCA core error info.\n");
- return;
- }
-
- if (xec > len) {
- pr_cont("Unrecognized %s MCA bank error code.\n",
- amd_core_mcablock_names[mca_type]);
- return;
- }
-
- pr_cont("%s.\n", error_desc_array[xec]);
-}
-
-static void decode_df_errors(u8 xec, unsigned int mca_type)
-{
- const char * const *error_desc_array;
- size_t len;
-
- pr_emerg(HW_ERR "Data Fabric Error: ");
-
- switch (mca_type) {
- case SMCA_CS:
- error_desc_array = f17h_cs_mce_desc;
- len = ARRAY_SIZE(f17h_cs_mce_desc) - 1;
- break;
-
- case SMCA_PIE:
- error_desc_array = f17h_pie_mce_desc;
- len = ARRAY_SIZE(f17h_pie_mce_desc) - 1;
- break;
-
- default:
- pr_cont("Corrupted MCA Data Fabric info.\n");
- return;
- }
-
- if (xec > len) {
- pr_cont("Unrecognized %s MCA bank error code.\n",
- amd_df_mcablock_names[mca_type]);
- return;
- }
-
- pr_cont("%s.\n", error_desc_array[xec]);
-}
-
/* Decode errors according to Scalable MCA specification */
static void decode_smca_errors(struct mce *m)
{
- u32 addr = MSR_AMD64_SMCA_MCx_IPID(m->bank);
- unsigned int hwid, mca_type, i;
- u8 xec = XEC(m->status, xec_mask);
- const char * const *error_desc_array;
+ struct smca_hwid_mcatype *type;
+ unsigned int bank_type;
const char *ip_name;
- u32 low, high;
- size_t len;
+ u8 xec = XEC(m->status, xec_mask);
- if (rdmsr_safe(addr, &low, &high)) {
- pr_emerg("Invalid IP block specified, error information is unreliable.\n");
+ if (m->bank >= ARRAY_SIZE(smca_banks))
return;
- }
-
- hwid = high & MCI_IPID_HWID;
- mca_type = (high & MCI_IPID_MCATYPE) >> 16;
-
- pr_emerg(HW_ERR "MC%d IPID value: 0x%08x%08x\n", m->bank, high, low);
-
- /*
- * Based on hwid and mca_type values, decode errors from respective IPs.
- * Note: mca_type values make sense only in the context of an hwid.
- */
- for (i = 0; i < ARRAY_SIZE(amd_hwids); i++)
- if (amd_hwids[i].hwid == hwid)
- break;
-
- switch (i) {
- case SMCA_F17H_CORE:
- ip_name = (mca_type == SMCA_L3_CACHE) ?
- "L3 Cache" : "F17h Core";
- return decode_f17h_core_errors(ip_name, xec, mca_type);
- break;
- case SMCA_DF:
- return decode_df_errors(xec, mca_type);
- break;
-
- case SMCA_UMC:
- error_desc_array = f17h_umc_mce_desc;
- len = ARRAY_SIZE(f17h_umc_mce_desc) - 1;
- break;
-
- case SMCA_PB:
- error_desc_array = f17h_pb_mce_desc;
- len = ARRAY_SIZE(f17h_pb_mce_desc) - 1;
- break;
+ if (boot_cpu_data.x86 >= 0x17 && m->bank == 4)
+ pr_emerg(HW_ERR "Bank 4 is reserved on Fam17h.\n");
- case SMCA_PSP:
- error_desc_array = f17h_psp_mce_desc;
- len = ARRAY_SIZE(f17h_psp_mce_desc) - 1;
- break;
-
- case SMCA_SMU:
- error_desc_array = f17h_smu_mce_desc;
- len = ARRAY_SIZE(f17h_smu_mce_desc) - 1;
- break;
-
- default:
- pr_emerg(HW_ERR "HWID:%d does not match any existing IPs.\n", hwid);
+ type = smca_banks[m->bank].type;
+ if (!type)
return;
- }
- ip_name = amd_hwids[i].name;
- pr_emerg(HW_ERR "%s Error: ", ip_name);
+ bank_type = type->bank_type;
+ ip_name = smca_bank_names[bank_type].long_name;
- if (xec > len) {
- pr_cont("Unrecognized %s MCA bank error code.\n", ip_name);
- return;
- }
+ pr_emerg(HW_ERR "%s Extended Error Code: %d\n", ip_name, xec);
- pr_cont("%s.\n", error_desc_array[xec]);
+ /* Only print the decode of valid error codes */
+ if (xec < smca_mce_descs[bank_type].num_descs &&
+ (type->xec_bitmap & BIT_ULL(xec))) {
+ pr_emerg(HW_ERR "%s Error: ", ip_name);
+ pr_cont("%s.\n", smca_mce_descs[bank_type].descs[xec]);
+ }
}
static inline void amd_decode_err_code(u16 ec)
@@ -1078,6 +966,8 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
u32 low, high;
u32 addr = MSR_AMD64_SMCA_MCx_CONFIG(m->bank);
+ pr_cont("|%s", ((m->status & MCI_STATUS_SYNDV) ? "SyndV" : "-"));
+
if (!rdmsr_safe(addr, &low, &high) &&
(low & MCI_CONFIG_MCAX))
pr_cont("|%s", ((m->status & MCI_STATUS_TCC) ? "TCC" : "-"));
@@ -1091,12 +981,20 @@ int amd_decode_mce(struct notifier_block *nb, unsigned long val, void *data)
pr_cont("]: 0x%016llx\n", m->status);
if (m->status & MCI_STATUS_ADDRV)
- pr_emerg(HW_ERR "MC%d Error Address: 0x%016llx\n", m->bank, m->addr);
+ pr_emerg(HW_ERR "Error Addr: 0x%016llx", m->addr);
if (boot_cpu_has(X86_FEATURE_SMCA)) {
+ if (m->status & MCI_STATUS_SYNDV)
+ pr_cont(", Syndrome: 0x%016llx", m->synd);
+
+ pr_cont(", IPID: 0x%016llx", m->ipid);
+
+ pr_cont("\n");
+
decode_smca_errors(m);
goto err_code;
- }
+ } else
+ pr_cont("\n");
if (!fam_ops)
goto err_code;
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index 3d89e60..04788d9 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -96,6 +96,12 @@ config EXTCON_PALMAS
Say Y here to enable support for USB peripheral and USB host
detection by palmas usb.
+config EXTCON_QCOM_SPMI_MISC
+ tristate "Qualcomm USB extcon support"
+ help
+ Say Y here to enable SPMI PMIC based USB cable detection
+ support on Qualcomm PMICs such as PM8941.
+
config EXTCON_RT8973A
tristate "Richtek RT8973A EXTCON support"
depends on I2C
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 972c813..31a0a99 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX77843) += extcon-max77843.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o
+obj-$(CONFIG_EXTCON_QCOM_SPMI_MISC) += extcon-qcom-spmi-misc.o
obj-$(CONFIG_EXTCON_RT8973A) += extcon-rt8973a.o
obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o
obj-$(CONFIG_EXTCON_USB_GPIO) += extcon-usb-gpio.o
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c
index 44e48aa..bc53870 100644
--- a/drivers/extcon/extcon-adc-jack.c
+++ b/drivers/extcon/extcon-adc-jack.c
@@ -3,6 +3,9 @@
*
* Analog Jack extcon driver with ADC-based detection capability.
*
+ * Copyright (C) 2016 Samsung Electronics
+ * Chanwoo Choi <cw00.choi@samsung.com>
+ *
* Copyright (C) 2012 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
@@ -58,7 +61,7 @@ static void adc_jack_handler(struct work_struct *work)
struct adc_jack_data *data = container_of(to_delayed_work(work),
struct adc_jack_data,
handler);
- u32 state = 0;
+ struct adc_jack_cond *def;
int ret, adc_val;
int i;
@@ -70,17 +73,18 @@ static void adc_jack_handler(struct work_struct *work)
/* Get state from adc value with adc_conditions */
for (i = 0; i < data->num_conditions; i++) {
- struct adc_jack_cond *def = &data->adc_conditions[i];
- if (!def->state)
- break;
+ def = &data->adc_conditions[i];
if (def->min_adc <= adc_val && def->max_adc >= adc_val) {
- state = def->state;
- break;
+ extcon_set_state_sync(data->edev, def->id, true);
+ return;
}
}
- /* if no def has met, it means state = 0 (no cables attached) */
- extcon_set_state(data->edev, state);
+ /* Set the detached state if adc value is not included in the range */
+ for (i = 0; i < data->num_conditions; i++) {
+ def = &data->adc_conditions[i];
+ extcon_set_state_sync(data->edev, def->id, false);
+ }
}
static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
@@ -114,16 +118,14 @@ static int adc_jack_probe(struct platform_device *pdev)
return -ENOMEM;
}
- if (!pdata->adc_conditions ||
- !pdata->adc_conditions[0].state) {
+ if (!pdata->adc_conditions) {
dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
return -EINVAL;
}
data->adc_conditions = pdata->adc_conditions;
/* Check the length of array and set num_conditions */
- for (i = 0; data->adc_conditions[i].state; i++)
- ;
+ for (i = 0; data->adc_conditions[i].id != EXTCON_NONE; i++);
data->num_conditions = i;
data->chan = iio_channel_get(&pdev->dev, pdata->consumer_channel);
@@ -158,6 +160,7 @@ static int adc_jack_probe(struct platform_device *pdev)
if (data->wakeup_source)
device_init_wakeup(&pdev->dev, 1);
+ adc_jack_handler(&data->handler.work);
return 0;
}
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c
index 1d8e0a5..56e6c4c 100644
--- a/drivers/extcon/extcon-arizona.c
+++ b/drivers/extcon/extcon-arizona.c
@@ -183,7 +183,7 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
if (clamp)
val = ARIZONA_RMV_SHRT_HP1L;
break;
- };
+ }
snd_soc_dapm_mutex_lock(arizona->dapm);
@@ -614,7 +614,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
}
/* If the cable was removed while measuring ignore the result */
- ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL);
+ ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret);
@@ -649,7 +649,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
else
report = EXTCON_JACK_HEADPHONE;
- ret = extcon_set_cable_state_(info->edev, report, true);
+ ret = extcon_set_state_sync(info->edev, report, true);
if (ret != 0)
dev_err(arizona->dev, "Failed to report HP/line: %d\n",
ret);
@@ -732,7 +732,7 @@ err:
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
- ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true);
+ ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
@@ -789,7 +789,7 @@ err:
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
- ret = extcon_set_cable_state_(info->edev, EXTCON_JACK_HEADPHONE, true);
+ ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
@@ -829,7 +829,7 @@ static void arizona_micd_detect(struct work_struct *work)
mutex_lock(&info->lock);
/* If the cable was removed while measuring ignore the result */
- ret = extcon_get_cable_state_(info->edev, EXTCON_MECHANICAL);
+ ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret);
@@ -914,7 +914,7 @@ static void arizona_micd_detect(struct work_struct *work)
arizona_identify_headphone(info);
- ret = extcon_set_cable_state_(info->edev,
+ ret = extcon_set_state_sync(info->edev,
EXTCON_JACK_MICROPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Headset report failed: %d\n",
@@ -1108,7 +1108,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
if (info->last_jackdet == present) {
dev_dbg(arizona->dev, "Detected jack\n");
- ret = extcon_set_cable_state_(info->edev,
+ ret = extcon_set_state_sync(info->edev,
EXTCON_MECHANICAL, true);
if (ret != 0)
@@ -1149,10 +1149,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
info->micd_ranges[i].key, 0);
input_sync(info->input);
- ret = extcon_update_state(info->edev, 0xffffffff, 0);
- if (ret != 0)
- dev_err(arizona->dev, "Removal report failed: %d\n",
- ret);
+ for (i = 0; i < ARRAY_SIZE(arizona_cable) - 1; i++) {
+ ret = extcon_set_state_sync(info->edev,
+ arizona_cable[i], false);
+ if (ret != 0)
+ dev_err(arizona->dev,
+ "Removal report failed: %d\n", ret);
+ }
regmap_update_bits(arizona->regmap,
ARIZONA_JACK_DETECT_DEBOUNCE,
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c
index fd55c2f..42f41e8 100644
--- a/drivers/extcon/extcon-axp288.c
+++ b/drivers/extcon/extcon-axp288.c
@@ -189,19 +189,19 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
switch (chrg_type) {
case DET_STAT_SDP:
- dev_dbg(info->dev, "sdp cable is connecetd\n");
+ dev_dbg(info->dev, "sdp cable is connected\n");
notify_otg = true;
notify_charger = true;
cable = EXTCON_CHG_USB_SDP;
break;
case DET_STAT_CDP:
- dev_dbg(info->dev, "cdp cable is connecetd\n");
+ dev_dbg(info->dev, "cdp cable is connected\n");
notify_otg = true;
notify_charger = true;
cable = EXTCON_CHG_USB_CDP;
break;
case DET_STAT_DCP:
- dev_dbg(info->dev, "dcp cable is connecetd\n");
+ dev_dbg(info->dev, "dcp cable is connected\n");
notify_charger = true;
cable = EXTCON_CHG_USB_DCP;
break;
@@ -226,7 +226,7 @@ notify_otg:
}
if (notify_charger)
- extcon_set_cable_state_(info->edev, cable, vbus_attach);
+ extcon_set_state_sync(info->edev, cable, vbus_attach);
/* Clear the flags on disconnect event */
if (!vbus_attach)
diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c
index d023789..ebed22f 100644
--- a/drivers/extcon/extcon-gpio.c
+++ b/drivers/extcon/extcon-gpio.c
@@ -49,7 +49,8 @@ static void gpio_extcon_work(struct work_struct *work)
state = gpiod_get_value_cansleep(data->id_gpiod);
if (data->pdata->gpio_active_low)
state = !state;
- extcon_set_state(data->edev, state);
+
+ extcon_set_state_sync(data->edev, data->pdata->extcon_id, state);
}
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c
index 852a711..12e26c4 100644
--- a/drivers/extcon/extcon-max14577.c
+++ b/drivers/extcon/extcon-max14577.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2013,2014 Samsung Electronics
* Chanwoo Choi <cw00.choi@samsung.com>
- * Krzysztof Kozlowski <k.kozlowski@samsung.com>
+ * Krzysztof Kozlowski <krzk@kernel.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -357,7 +357,7 @@ static int max14577_muic_jig_handler(struct max14577_muic_info *info,
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
+ extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
return 0;
}
@@ -454,24 +454,24 @@ static int max14577_muic_chg_handler(struct max14577_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_USB, attached);
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
break;
case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
attached);
break;
case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
attached);
break;
case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
attached);
break;
case MAX14577_CHARGER_TYPE_SPECIAL_1A:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
attached);
break;
case MAX14577_CHARGER_TYPE_NONE:
@@ -791,6 +791,6 @@ static struct platform_driver max14577_muic_driver = {
module_platform_driver(max14577_muic_driver);
MODULE_DESCRIPTION("Maxim 14577/77836 Extcon driver");
-MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <k.kozlowski@samsung.com>");
+MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>, Krzysztof Kozlowski <krzk@kernel.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:extcon-max14577");
diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c
index c24abec..533e16a 100644
--- a/drivers/extcon/extcon-max3355.c
+++ b/drivers/extcon/extcon-max3355.c
@@ -39,16 +39,16 @@ static irqreturn_t max3355_id_irq(int irq, void *dev_id)
* As we don't have event for USB peripheral cable attached,
* we simulate USB peripheral attach here.
*/
- extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, false);
- extcon_set_cable_state_(data->edev, EXTCON_USB, true);
+ extcon_set_state_sync(data->edev, EXTCON_USB_HOST, false);
+ extcon_set_state_sync(data->edev, EXTCON_USB, true);
} else {
/*
* ID = 0 means USB HOST cable attached.
* As we don't have event for USB peripheral cable detached,
* we simulate USB peripheral detach here.
*/
- extcon_set_cable_state_(data->edev, EXTCON_USB, false);
- extcon_set_cable_state_(data->edev, EXTCON_USB_HOST, true);
+ extcon_set_state_sync(data->edev, EXTCON_USB, false);
+ extcon_set_state_sync(data->edev, EXTCON_USB_HOST, true);
}
return IRQ_HANDLED;
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index f17cb76..68dbcb8 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -505,8 +505,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached);
- extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
+ extcon_set_state_sync(info->edev, EXTCON_DOCK, attached);
+ extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
goto out;
case MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE: /* Dock-Desk */
dock_id = EXTCON_DOCK;
@@ -514,8 +514,8 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD: /* Dock-Audio */
dock_id = EXTCON_DOCK;
if (!attached) {
- extcon_set_cable_state_(info->edev, EXTCON_USB, false);
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_USB, false);
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
false);
}
break;
@@ -530,7 +530,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
attached);
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, dock_id, attached);
+ extcon_set_state_sync(info->edev, dock_id, attached);
out:
return 0;
@@ -596,7 +596,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
attached);
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
+ extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached);
break;
case MAX77693_MUIC_GND_AV_CABLE_LOAD:
/* Audio Video Cable with load, PATH:AUDIO */
@@ -604,14 +604,14 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
attached);
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_USB, attached);
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
break;
case MAX77693_MUIC_GND_MHL:
case MAX77693_MUIC_GND_MHL_VB:
/* MHL or MHL with USB/TA cable */
- extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
+ extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
break;
default:
dev_err(info->dev, "failed to detect %s cable of gnd type\n",
@@ -653,7 +653,7 @@ static int max77693_muic_jig_handler(struct max77693_muic_info *info,
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
+ extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
return 0;
}
@@ -807,10 +807,10 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
* - Support charging through micro-usb port without
* data connection
*/
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
attached);
if (!cable_attached)
- extcon_set_cable_state_(info->edev,
+ extcon_set_state_sync(info->edev,
EXTCON_DISP_MHL, cable_attached);
break;
}
@@ -834,13 +834,13 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
* - Support charging through micro-usb port without
* data connection.
*/
- extcon_set_cable_state_(info->edev, EXTCON_USB,
+ extcon_set_state_sync(info->edev, EXTCON_USB,
attached);
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
if (!cable_attached)
- extcon_set_cable_state_(info->edev, EXTCON_DOCK,
+ extcon_set_state_sync(info->edev, EXTCON_DOCK,
cable_attached);
break;
case MAX77693_MUIC_ADC_RESERVED_ACC_3: /* Dock-Smart */
@@ -869,9 +869,9 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_DOCK,
+ extcon_set_state_sync(info->edev, EXTCON_DOCK,
attached);
- extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL,
+ extcon_set_state_sync(info->edev, EXTCON_DISP_MHL,
attached);
break;
}
@@ -905,28 +905,28 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_USB,
+ extcon_set_state_sync(info->edev, EXTCON_USB,
attached);
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
break;
case MAX77693_CHARGER_TYPE_DEDICATED_CHG:
/* Only TA cable */
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
attached);
break;
}
break;
case MAX77693_CHARGER_TYPE_DOWNSTREAM_PORT:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
attached);
break;
case MAX77693_CHARGER_TYPE_APPLE_500MA:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
attached);
break;
case MAX77693_CHARGER_TYPE_APPLE_1A_2A:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
attached);
break;
case MAX77693_CHARGER_TYPE_DEAD_BATTERY:
diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c
index b188bd6..5d11fdf 100644
--- a/drivers/extcon/extcon-max77843.c
+++ b/drivers/extcon/extcon-max77843.c
@@ -346,7 +346,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
+ extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached);
break;
case MAX77843_MUIC_GND_MHL_VB:
case MAX77843_MUIC_GND_MHL:
@@ -356,7 +356,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
+ extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
break;
default:
dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n",
@@ -392,7 +392,7 @@ static int max77843_muic_jig_handler(struct max77843_muic_info *info,
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
+ extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
return 0;
}
@@ -486,8 +486,8 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_USB, attached);
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
break;
case MAX77843_MUIC_CHG_DOWNSTREAM:
@@ -497,7 +497,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
attached);
break;
case MAX77843_MUIC_CHG_DEDICATED:
@@ -507,7 +507,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
attached);
break;
case MAX77843_MUIC_CHG_SPECIAL_500MA:
@@ -517,7 +517,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
attached);
break;
case MAX77843_MUIC_CHG_SPECIAL_1A:
@@ -527,7 +527,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
if (ret < 0)
return ret;
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
attached);
break;
case MAX77843_MUIC_CHG_GND:
@@ -536,10 +536,10 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info)
/* Charger cable on MHL accessory is attach or detach */
if (gnd_type == MAX77843_MUIC_GND_MHL_VB)
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
true);
else if (gnd_type == MAX77843_MUIC_GND_MHL)
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
false);
break;
case MAX77843_MUIC_CHG_NONE:
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index 9a89320..4a0612f 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -331,11 +331,11 @@ static int max8997_muic_handle_usb(struct max8997_muic_info *info,
switch (usb_type) {
case MAX8997_USB_HOST:
- extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, attached);
+ extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached);
break;
case MAX8997_USB_DEVICE:
- extcon_set_cable_state_(info->edev, EXTCON_USB, attached);
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_USB, attached);
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
break;
default:
@@ -361,7 +361,7 @@ static int max8997_muic_handle_dock(struct max8997_muic_info *info,
switch (cable_type) {
case MAX8997_MUIC_ADC_AV_CABLE_NOLOAD:
case MAX8997_MUIC_ADC_FACTORY_MODE_UART_ON:
- extcon_set_cable_state_(info->edev, EXTCON_DOCK, attached);
+ extcon_set_state_sync(info->edev, EXTCON_DOCK, attached);
break;
default:
dev_err(info->dev, "failed to detect %s dock device\n",
@@ -384,7 +384,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
return ret;
}
- extcon_set_cable_state_(info->edev, EXTCON_JIG, attached);
+ extcon_set_state_sync(info->edev, EXTCON_JIG, attached);
return 0;
}
@@ -406,7 +406,7 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info)
return ret;
break;
case MAX8997_MUIC_ADC_MHL:
- extcon_set_cable_state_(info->edev, EXTCON_DISP_MHL, attached);
+ extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached);
break;
case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF:
case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON:
@@ -489,19 +489,19 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info)
}
break;
case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_CDP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_CDP,
attached);
break;
case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_DCP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP,
attached);
break;
case MAX8997_CHARGER_TYPE_500MA:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SLOW,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SLOW,
attached);
break;
case MAX8997_CHARGER_TYPE_1A:
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_FAST,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_FAST,
attached);
break;
default:
diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c
index caff46c..634ba70 100644
--- a/drivers/extcon/extcon-palmas.c
+++ b/drivers/extcon/extcon-palmas.c
@@ -61,7 +61,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) {
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
- extcon_set_cable_state_(edev, EXTCON_USB, true);
+ extcon_set_state_sync(edev, EXTCON_USB, true);
dev_info(palmas_usb->dev, "USB cable is attached\n");
} else {
dev_dbg(palmas_usb->dev,
@@ -70,7 +70,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
} else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) {
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
- extcon_set_cable_state_(edev, EXTCON_USB, false);
+ extcon_set_state_sync(edev, EXTCON_USB, false);
dev_info(palmas_usb->dev, "USB cable is detached\n");
} else {
dev_dbg(palmas_usb->dev,
@@ -98,7 +98,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
- extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
@@ -106,17 +106,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
- extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
- extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
- extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
}
@@ -137,10 +137,10 @@ static void palmas_gpio_id_detect(struct work_struct *work)
id = gpiod_get_value_cansleep(palmas_usb->id_gpiod);
if (id) {
- extcon_set_cable_state_(edev, EXTCON_USB_HOST, false);
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else {
- extcon_set_cable_state_(edev, EXTCON_USB_HOST, true);
+ extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
}
}
diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c
new file mode 100644
index 0000000..ca957a5
--- /dev/null
+++ b/drivers/extcon/extcon-qcom-spmi-misc.c
@@ -0,0 +1,170 @@
+/**
+ * extcon-qcom-spmi-misc.c - Qualcomm USB extcon driver to support USB ID
+ * detection based on extcon-usb-gpio.c.
+ *
+ * Copyright (C) 2016 Linaro, Ltd.
+ * Stephen Boyd <stephen.boyd@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/extcon.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define USB_ID_DEBOUNCE_MS 5 /* ms */
+
+struct qcom_usb_extcon_info {
+ struct extcon_dev *edev;
+ int irq;
+ struct delayed_work wq_detcable;
+ unsigned long debounce_jiffies;
+};
+
+static const unsigned int qcom_usb_extcon_cable[] = {
+ EXTCON_USB_HOST,
+ EXTCON_NONE,
+};
+
+static void qcom_usb_extcon_detect_cable(struct work_struct *work)
+{
+ bool id;
+ int ret;
+ struct qcom_usb_extcon_info *info = container_of(to_delayed_work(work),
+ struct qcom_usb_extcon_info,
+ wq_detcable);
+
+ /* check ID and update cable state */
+ ret = irq_get_irqchip_state(info->irq, IRQCHIP_STATE_LINE_LEVEL, &id);
+ if (ret)
+ return;
+
+ extcon_set_state(info->edev, EXTCON_USB_HOST, !id);
+}
+
+static irqreturn_t qcom_usb_irq_handler(int irq, void *dev_id)
+{
+ struct qcom_usb_extcon_info *info = dev_id;
+
+ queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
+ info->debounce_jiffies);
+
+ return IRQ_HANDLED;
+}
+
+static int qcom_usb_extcon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct qcom_usb_extcon_info *info;
+ int ret;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->edev = devm_extcon_dev_allocate(dev, qcom_usb_extcon_cable);
+ if (IS_ERR(info->edev)) {
+ dev_err(dev, "failed to allocate extcon device\n");
+ return -ENOMEM;
+ }
+
+ ret = devm_extcon_dev_register(dev, info->edev);
+ if (ret < 0) {
+ dev_err(dev, "failed to register extcon device\n");
+ return ret;
+ }
+
+ info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS);
+ INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable);
+
+ info->irq = platform_get_irq_byname(pdev, "usb_id");
+ if (info->irq < 0)
+ return info->irq;
+
+ ret = devm_request_threaded_irq(dev, info->irq, NULL,
+ qcom_usb_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ pdev->name, info);
+ if (ret < 0) {
+ dev_err(dev, "failed to request handler for ID IRQ\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, info);
+ device_init_wakeup(dev, 1);
+
+ /* Perform initial detection */
+ qcom_usb_extcon_detect_cable(&info->wq_detcable.work);
+
+ return 0;
+}
+
+static int qcom_usb_extcon_remove(struct platform_device *pdev)
+{
+ struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev);
+
+ cancel_delayed_work_sync(&info->wq_detcable);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qcom_usb_extcon_suspend(struct device *dev)
+{
+ struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (device_may_wakeup(dev))
+ ret = enable_irq_wake(info->irq);
+
+ return ret;
+}
+
+static int qcom_usb_extcon_resume(struct device *dev)
+{
+ struct qcom_usb_extcon_info *info = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (device_may_wakeup(dev))
+ ret = disable_irq_wake(info->irq);
+
+ return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qcom_usb_extcon_pm_ops,
+ qcom_usb_extcon_suspend, qcom_usb_extcon_resume);
+
+static const struct of_device_id qcom_usb_extcon_dt_match[] = {
+ { .compatible = "qcom,pm8941-misc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match);
+
+static struct platform_driver qcom_usb_extcon_driver = {
+ .probe = qcom_usb_extcon_probe,
+ .remove = qcom_usb_extcon_remove,
+ .driver = {
+ .name = "extcon-pm8941-misc",
+ .pm = &qcom_usb_extcon_pm_ops,
+ .of_match_table = qcom_usb_extcon_dt_match,
+ },
+};
+module_platform_driver(qcom_usb_extcon_driver);
+
+MODULE_DESCRIPTION("QCOM USB ID extcon driver");
+MODULE_AUTHOR("Stephen Boyd <stephen.boyd@linaro.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c
index 97e074d..174c388 100644
--- a/drivers/extcon/extcon-rt8973a.c
+++ b/drivers/extcon/extcon-rt8973a.c
@@ -398,9 +398,9 @@ static int rt8973a_muic_cable_handler(struct rt8973a_muic_info *info,
return ret;
/* Change the state of external accessory */
- extcon_set_cable_state_(info->edev, id, attached);
+ extcon_set_state_sync(info->edev, id, attached);
if (id == EXTCON_USB)
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
return 0;
diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c
index df769a1..b223256 100644
--- a/drivers/extcon/extcon-sm5502.c
+++ b/drivers/extcon/extcon-sm5502.c
@@ -411,9 +411,9 @@ static int sm5502_muic_cable_handler(struct sm5502_muic_info *info,
return ret;
/* Change the state of external accessory */
- extcon_set_cable_state_(info->edev, id, attached);
+ extcon_set_state_sync(info->edev, id, attached);
if (id == EXTCON_USB)
- extcon_set_cable_state_(info->edev, EXTCON_CHG_USB_SDP,
+ extcon_set_state_sync(info->edev, EXTCON_CHG_USB_SDP,
attached);
return 0;
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
index 2512660..a27d350 100644
--- a/drivers/extcon/extcon-usb-gpio.c
+++ b/drivers/extcon/extcon-usb-gpio.c
@@ -63,16 +63,16 @@ static void usb_extcon_detect_cable(struct work_struct *work)
* As we don't have event for USB peripheral cable attached,
* we simulate USB peripheral attach here.
*/
- extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, false);
- extcon_set_cable_state_(info->edev, EXTCON_USB, true);
+ extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
+ extcon_set_state_sync(info->edev, EXTCON_USB, true);
} else {
/*
* ID = 0 means USB HOST cable attached.
* As we don't have event for USB peripheral cable detached,
* we simulate USB peripheral detach here.
*/
- extcon_set_cable_state_(info->edev, EXTCON_USB, false);
- extcon_set_cable_state_(info->edev, EXTCON_USB_HOST, true);
+ extcon_set_state_sync(info->edev, EXTCON_USB, false);
+ extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
}
}
diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
index 8682efc..7829846 100644
--- a/drivers/extcon/extcon.c
+++ b/drivers/extcon/extcon.c
@@ -38,43 +38,159 @@
#define SUPPORTED_CABLE_MAX 32
#define CABLE_NAME_MAX 30
-static const char *extcon_name[] = {
- [EXTCON_NONE] = "NONE",
+struct __extcon_info {
+ unsigned int type;
+ unsigned int id;
+ const char *name;
+
+} extcon_info[] = {
+ [EXTCON_NONE] = {
+ .type = EXTCON_TYPE_MISC,
+ .id = EXTCON_NONE,
+ .name = "NONE",
+ },
/* USB external connector */
- [EXTCON_USB] = "USB",
- [EXTCON_USB_HOST] = "USB-HOST",
+ [EXTCON_USB] = {
+ .type = EXTCON_TYPE_USB,
+ .id = EXTCON_USB,
+ .name = "USB",
+ },
+ [EXTCON_USB_HOST] = {
+ .type = EXTCON_TYPE_USB,
+ .id = EXTCON_USB_HOST,
+ .name = "USB_HOST",
+ },
/* Charging external connector */
- [EXTCON_CHG_USB_SDP] = "SDP",
- [EXTCON_CHG_USB_DCP] = "DCP",
- [EXTCON_CHG_USB_CDP] = "CDP",
- [EXTCON_CHG_USB_ACA] = "ACA",
- [EXTCON_CHG_USB_FAST] = "FAST-CHARGER",
- [EXTCON_CHG_USB_SLOW] = "SLOW-CHARGER",
+ [EXTCON_CHG_USB_SDP] = {
+ .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+ .id = EXTCON_CHG_USB_SDP,
+ .name = "SDP",
+ },
+ [EXTCON_CHG_USB_DCP] = {
+ .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+ .id = EXTCON_CHG_USB_DCP,
+ .name = "DCP",
+ },
+ [EXTCON_CHG_USB_CDP] = {
+ .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+ .id = EXTCON_CHG_USB_CDP,
+ .name = "CDP",
+ },
+ [EXTCON_CHG_USB_ACA] = {
+ .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+ .id = EXTCON_CHG_USB_ACA,
+ .name = "ACA",
+ },
+ [EXTCON_CHG_USB_FAST] = {
+ .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+ .id = EXTCON_CHG_USB_FAST,
+ .name = "FAST-CHARGER",
+ },
+ [EXTCON_CHG_USB_SLOW] = {
+ .type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
+ .id = EXTCON_CHG_USB_SLOW,
+ .name = "SLOW-CHARGER",
+ },
+ [EXTCON_CHG_WPT] = {
+ .type = EXTCON_TYPE_CHG,
+ .id = EXTCON_CHG_WPT,
+ .name = "WPT",
+ },
/* Jack external connector */
- [EXTCON_JACK_MICROPHONE] = "MICROPHONE",
- [EXTCON_JACK_HEADPHONE] = "HEADPHONE",
- [EXTCON_JACK_LINE_IN] = "LINE-IN",
- [EXTCON_JACK_LINE_OUT] = "LINE-OUT",
- [EXTCON_JACK_VIDEO_IN] = "VIDEO-IN",
- [EXTCON_JACK_VIDEO_OUT] = "VIDEO-OUT",
- [EXTCON_JACK_SPDIF_IN] = "SPDIF-IN",
- [EXTCON_JACK_SPDIF_OUT] = "SPDIF-OUT",
+ [EXTCON_JACK_MICROPHONE] = {
+ .type = EXTCON_TYPE_JACK,
+ .id = EXTCON_JACK_MICROPHONE,
+ .name = "MICROPHONE",
+ },
+ [EXTCON_JACK_HEADPHONE] = {
+ .type = EXTCON_TYPE_JACK,
+ .id = EXTCON_JACK_HEADPHONE,
+ .name = "HEADPHONE",
+ },
+ [EXTCON_JACK_LINE_IN] = {
+ .type = EXTCON_TYPE_JACK,
+ .id = EXTCON_JACK_LINE_IN,
+ .name = "LINE-IN",
+ },
+ [EXTCON_JACK_LINE_OUT] = {
+ .type = EXTCON_TYPE_JACK,
+ .id = EXTCON_JACK_LINE_OUT,
+ .name = "LINE-OUT",
+ },
+ [EXTCON_JACK_VIDEO_IN] = {
+ .type = EXTCON_TYPE_JACK,
+ .id = EXTCON_JACK_VIDEO_IN,
+ .name = "VIDEO-IN",
+ },
+ [EXTCON_JACK_VIDEO_OUT] = {
+ .type = EXTCON_TYPE_JACK,
+ .id = EXTCON_JACK_VIDEO_OUT,
+ .name = "VIDEO-OUT",
+ },
+ [EXTCON_JACK_SPDIF_IN] = {
+ .type = EXTCON_TYPE_JACK,
+ .id = EXTCON_JACK_SPDIF_IN,
+ .name = "SPDIF-IN",
+ },
+ [EXTCON_JACK_SPDIF_OUT] = {
+ .type = EXTCON_TYPE_JACK,
+ .id = EXTCON_JACK_SPDIF_OUT,
+ .name = "SPDIF-OUT",
+ },
/* Display external connector */
- [EXTCON_DISP_HDMI] = "HDMI",
- [EXTCON_DISP_MHL] = "MHL",
- [EXTCON_DISP_DVI] = "DVI",
- [EXTCON_DISP_VGA] = "VGA",
+ [EXTCON_DISP_HDMI] = {
+ .type = EXTCON_TYPE_DISP,
+ .id = EXTCON_DISP_HDMI,
+ .name = "HDMI",
+ },
+ [EXTCON_DISP_MHL] = {
+ .type = EXTCON_TYPE_DISP,
+ .id = EXTCON_DISP_MHL,
+ .name = "MHL",
+ },
+ [EXTCON_DISP_DVI] = {
+ .type = EXTCON_TYPE_DISP,
+ .id = EXTCON_DISP_DVI,
+ .name = "DVI",
+ },
+ [EXTCON_DISP_VGA] = {
+ .type = EXTCON_TYPE_DISP,
+ .id = EXTCON_DISP_VGA,
+ .name = "VGA",
+ },
+ [EXTCON_DISP_DP] = {
+ .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+ .id = EXTCON_DISP_DP,
+ .name = "DP",
+ },
+ [EXTCON_DISP_HMD] = {
+ .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+ .id = EXTCON_DISP_HMD,
+ .name = "HMD",
+ },
/* Miscellaneous external connector */
- [EXTCON_DOCK] = "DOCK",
- [EXTCON_JIG] = "JIG",
- [EXTCON_MECHANICAL] = "MECHANICAL",
-
- NULL,
+ [EXTCON_DOCK] = {
+ .type = EXTCON_TYPE_MISC,
+ .id = EXTCON_DOCK,
+ .name = "DOCK",
+ },
+ [EXTCON_JIG] = {
+ .type = EXTCON_TYPE_MISC,
+ .id = EXTCON_JIG,
+ .name = "JIG",
+ },
+ [EXTCON_MECHANICAL] = {
+ .type = EXTCON_TYPE_MISC,
+ .id = EXTCON_MECHANICAL,
+ .name = "MECHANICAL",
+ },
+
+ { /* sentinel */ }
};
/**
@@ -95,6 +211,16 @@ struct extcon_cable {
struct device_attribute attr_state;
struct attribute *attrs[3]; /* to be fed to attr_g.attrs */
+
+ union extcon_property_value usb_propval[EXTCON_PROP_USB_CNT];
+ union extcon_property_value chg_propval[EXTCON_PROP_CHG_CNT];
+ union extcon_property_value jack_propval[EXTCON_PROP_JACK_CNT];
+ union extcon_property_value disp_propval[EXTCON_PROP_DISP_CNT];
+
+ unsigned long usb_bits[BITS_TO_LONGS(EXTCON_PROP_USB_CNT)];
+ unsigned long chg_bits[BITS_TO_LONGS(EXTCON_PROP_CHG_CNT)];
+ unsigned long jack_bits[BITS_TO_LONGS(EXTCON_PROP_JACK_CNT)];
+ unsigned long disp_bits[BITS_TO_LONGS(EXTCON_PROP_DISP_CNT)];
};
static struct class *extcon_class;
@@ -147,14 +273,93 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id
return -EINVAL;
}
-static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached)
+static int get_extcon_type(unsigned int prop)
{
- if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) {
- *attached = ((new >> idx) & 0x1) ? true : false;
- return true;
+ switch (prop) {
+ case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+ return EXTCON_TYPE_USB;
+ case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+ return EXTCON_TYPE_CHG;
+ case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+ return EXTCON_TYPE_JACK;
+ case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+ return EXTCON_TYPE_DISP;
+ default:
+ return -EINVAL;
}
+}
+
+static bool is_extcon_attached(struct extcon_dev *edev, unsigned int index)
+{
+ return !!(edev->state & BIT(index));
+}
+
+static bool is_extcon_changed(struct extcon_dev *edev, int index,
+ bool new_state)
+{
+ int state = !!(edev->state & BIT(index));
+ return (state != new_state);
+}
+
+static bool is_extcon_property_supported(unsigned int id, unsigned int prop)
+{
+ int type;
+
+ /* Check whether the property is supported or not. */
+ type = get_extcon_type(prop);
+ if (type < 0)
+ return false;
- return false;
+ /* Check whether a specific extcon id supports the property or not. */
+ return !!(extcon_info[id].type & type);
+}
+
+static int is_extcon_property_capability(struct extcon_dev *edev,
+ unsigned int id, int index,unsigned int prop)
+{
+ struct extcon_cable *cable;
+ int type, ret;
+
+ /* Check whether the property is supported or not. */
+ type = get_extcon_type(prop);
+ if (type < 0)
+ return type;
+
+ cable = &edev->cables[index];
+
+ switch (type) {
+ case EXTCON_TYPE_USB:
+ ret = test_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
+ break;
+ case EXTCON_TYPE_CHG:
+ ret = test_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
+ break;
+ case EXTCON_TYPE_JACK:
+ ret = test_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
+ break;
+ case EXTCON_TYPE_DISP:
+ ret = test_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void init_property(struct extcon_dev *edev, unsigned int id, int index)
+{
+ unsigned int type = extcon_info[id].type;
+ struct extcon_cable *cable = &edev->cables[index];
+
+ if (EXTCON_TYPE_USB & type)
+ memset(cable->usb_propval, 0, sizeof(cable->usb_propval));
+ if (EXTCON_TYPE_CHG & type)
+ memset(cable->chg_propval, 0, sizeof(cable->chg_propval));
+ if (EXTCON_TYPE_JACK & type)
+ memset(cable->jack_propval, 0, sizeof(cable->jack_propval));
+ if (EXTCON_TYPE_DISP & type)
+ memset(cable->disp_propval, 0, sizeof(cable->disp_propval));
}
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
@@ -168,32 +373,13 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
for (i = 0; i < edev->max_supported; i++) {
count += sprintf(buf + count, "%s=%d\n",
- extcon_name[edev->supported_cable[i]],
+ extcon_info[edev->supported_cable[i]].name,
!!(edev->state & (1 << i)));
}
return count;
}
-
-static ssize_t state_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- u32 state;
- ssize_t ret = 0;
- struct extcon_dev *edev = dev_get_drvdata(dev);
-
- ret = sscanf(buf, "0x%x", &state);
- if (ret == 0)
- ret = -EINVAL;
- else
- ret = extcon_set_state(edev, state);
-
- if (ret < 0)
- return ret;
-
- return count;
-}
-static DEVICE_ATTR_RW(state);
+static DEVICE_ATTR_RO(state);
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -212,7 +398,7 @@ static ssize_t cable_name_show(struct device *dev,
int i = cable->cable_index;
return sprintf(buf, "%s\n",
- extcon_name[cable->edev->supported_cable[i]]);
+ extcon_info[cable->edev->supported_cable[i]].name);
}
static ssize_t cable_state_show(struct device *dev,
@@ -224,26 +410,17 @@ static ssize_t cable_state_show(struct device *dev,
int i = cable->cable_index;
return sprintf(buf, "%d\n",
- extcon_get_cable_state_(cable->edev,
- cable->edev->supported_cable[i]));
+ extcon_get_state(cable->edev, cable->edev->supported_cable[i]));
}
/**
- * extcon_update_state() - Update the cable attach states of the extcon device
- * only for the masked bits.
- * @edev: the extcon device
- * @mask: the bit mask to designate updated bits.
- * @state: new cable attach status for @edev
- *
- * Changing the state sends uevent with environment variable containing
- * the name of extcon device (envp[0]) and the state output (envp[1]).
- * Tizen uses this format for extcon device to get events from ports.
- * Android uses this format as well.
+ * extcon_sync() - Synchronize the states for both the attached/detached
+ * @edev: the extcon device that has the cable.
*
- * Note that the notifier provides which bits are changed in the state
- * variable with the val parameter (second) to the callback.
+ * This function send a notification to synchronize the all states of a
+ * specific external connector
*/
-int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
+int extcon_sync(struct extcon_dev *edev, unsigned int id)
{
char name_buf[120];
char state_buf[120];
@@ -252,100 +429,102 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
int env_offset = 0;
int length;
int index;
+ int state;
unsigned long flags;
- bool attached;
if (!edev)
return -EINVAL;
- spin_lock_irqsave(&edev->lock, flags);
+ index = find_cable_index_by_id(edev, id);
+ if (index < 0)
+ return index;
- if (edev->state != ((edev->state & ~mask) | (state & mask))) {
- u32 old_state;
+ spin_lock_irqsave(&edev->lock, flags);
- if (check_mutually_exclusive(edev, (edev->state & ~mask) |
- (state & mask))) {
- spin_unlock_irqrestore(&edev->lock, flags);
- return -EPERM;
- }
+ state = !!(edev->state & BIT(index));
+ raw_notifier_call_chain(&edev->nh[index], state, edev);
- old_state = edev->state;
- edev->state &= ~mask;
- edev->state |= state & mask;
+ /* This could be in interrupt handler */
+ prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
+ if (!prop_buf) {
+ /* Unlock early before uevent */
+ spin_unlock_irqrestore(&edev->lock, flags);
- for (index = 0; index < edev->max_supported; index++) {
- if (is_extcon_changed(old_state, edev->state, index,
- &attached))
- raw_notifier_call_chain(&edev->nh[index],
- attached, edev);
- }
+ dev_err(&edev->dev, "out of memory in extcon_set_state\n");
+ kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
- /* This could be in interrupt handler */
- prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
- if (prop_buf) {
- length = name_show(&edev->dev, NULL, prop_buf);
- if (length > 0) {
- if (prop_buf[length - 1] == '\n')
- prop_buf[length - 1] = 0;
- snprintf(name_buf, sizeof(name_buf),
- "NAME=%s", prop_buf);
- envp[env_offset++] = name_buf;
- }
- length = state_show(&edev->dev, NULL, prop_buf);
- if (length > 0) {
- if (prop_buf[length - 1] == '\n')
- prop_buf[length - 1] = 0;
- snprintf(state_buf, sizeof(state_buf),
- "STATE=%s", prop_buf);
- envp[env_offset++] = state_buf;
- }
- envp[env_offset] = NULL;
- /* Unlock early before uevent */
- spin_unlock_irqrestore(&edev->lock, flags);
+ return 0;
+ }
- kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
- free_page((unsigned long)prop_buf);
- } else {
- /* Unlock early before uevent */
- spin_unlock_irqrestore(&edev->lock, flags);
+ length = name_show(&edev->dev, NULL, prop_buf);
+ if (length > 0) {
+ if (prop_buf[length - 1] == '\n')
+ prop_buf[length - 1] = 0;
+ snprintf(name_buf, sizeof(name_buf), "NAME=%s", prop_buf);
+ envp[env_offset++] = name_buf;
+ }
- dev_err(&edev->dev, "out of memory in extcon_set_state\n");
- kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
- }
- } else {
- /* No changes */
- spin_unlock_irqrestore(&edev->lock, flags);
+ length = state_show(&edev->dev, NULL, prop_buf);
+ if (length > 0) {
+ if (prop_buf[length - 1] == '\n')
+ prop_buf[length - 1] = 0;
+ snprintf(state_buf, sizeof(state_buf), "STATE=%s", prop_buf);
+ envp[env_offset++] = state_buf;
}
+ envp[env_offset] = NULL;
+
+ /* Unlock early before uevent */
+ spin_unlock_irqrestore(&edev->lock, flags);
+ kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
+ free_page((unsigned long)prop_buf);
return 0;
}
-EXPORT_SYMBOL_GPL(extcon_update_state);
+EXPORT_SYMBOL_GPL(extcon_sync);
/**
- * extcon_set_state() - Set the cable attach states of the extcon device.
- * @edev: the extcon device
- * @state: new cable attach status for @edev
- *
- * Note that notifier provides which bits are changed in the state
- * variable with the val parameter (second) to the callback.
+ * extcon_get_state() - Get the state of a external connector.
+ * @edev: the extcon device that has the cable.
+ * @id: the unique id of each external connector in extcon enumeration.
*/
-int extcon_set_state(struct extcon_dev *edev, u32 state)
+int extcon_get_state(struct extcon_dev *edev, const unsigned int id)
{
+ int index, state;
+ unsigned long flags;
+
if (!edev)
return -EINVAL;
- return extcon_update_state(edev, 0xffffffff, state);
+ index = find_cable_index_by_id(edev, id);
+ if (index < 0)
+ return index;
+
+ spin_lock_irqsave(&edev->lock, flags);
+ state = is_extcon_attached(edev, index);
+ spin_unlock_irqrestore(&edev->lock, flags);
+
+ return state;
}
-EXPORT_SYMBOL_GPL(extcon_set_state);
+EXPORT_SYMBOL_GPL(extcon_get_state);
/**
- * extcon_get_cable_state_() - Get the status of a specific cable.
- * @edev: the extcon device that has the cable.
- * @id: the unique id of each external connector in extcon enumeration.
+ * extcon_set_state() - Set the state of a external connector.
+ * without a notification.
+ * @edev: the extcon device that has the cable.
+ * @id: the unique id of each external connector
+ * in extcon enumeration.
+ * @state: the new cable status. The default semantics is
+ * true: attached / false: detached.
+ *
+ * This function only set the state of a external connector without
+ * a notification. To synchronize the data of a external connector,
+ * use extcon_set_state_sync() and extcon_sync().
*/
-int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
+int extcon_set_state(struct extcon_dev *edev, unsigned int id,
+ bool cable_state)
{
- int index;
+ unsigned long flags;
+ int index, ret = 0;
if (!edev)
return -EINVAL;
@@ -354,41 +533,338 @@ int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id)
if (index < 0)
return index;
- if (edev->max_supported && edev->max_supported <= index)
- return -EINVAL;
+ spin_lock_irqsave(&edev->lock, flags);
+
+ /* Check whether the external connector's state is changed. */
+ if (!is_extcon_changed(edev, index, cable_state))
+ goto out;
+
+ if (check_mutually_exclusive(edev,
+ (edev->state & ~BIT(index)) | (cable_state & BIT(index)))) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ /*
+ * Initialize the value of extcon property before setting
+ * the detached state for an external connector.
+ */
+ if (!cable_state)
+ init_property(edev, id, index);
+
+ /* Update the state for a external connector. */
+ if (cable_state)
+ edev->state |= BIT(index);
+ else
+ edev->state &= ~(BIT(index));
+out:
+ spin_unlock_irqrestore(&edev->lock, flags);
- return !!(edev->state & (1 << index));
+ return ret;
}
-EXPORT_SYMBOL_GPL(extcon_get_cable_state_);
+EXPORT_SYMBOL_GPL(extcon_set_state);
/**
- * extcon_set_cable_state_() - Set the status of a specific cable.
+ * extcon_set_state_sync() - Set the state of a external connector
+ * with a notification.
* @edev: the extcon device that has the cable.
* @id: the unique id of each external connector
* in extcon enumeration.
* @state: the new cable status. The default semantics is
* true: attached / false: detached.
+ *
+ * This function set the state of external connector and synchronize the data
+ * by usning a notification.
*/
-int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
+int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
bool cable_state)
{
- u32 state;
+ int ret, index;
+ unsigned long flags;
+
+ index = find_cable_index_by_id(edev, id);
+ if (index < 0)
+ return index;
+
+ /* Check whether the external connector's state is changed. */
+ spin_lock_irqsave(&edev->lock, flags);
+ ret = is_extcon_changed(edev, index, cable_state);
+ spin_unlock_irqrestore(&edev->lock, flags);
+ if (!ret)
+ return 0;
+
+ ret = extcon_set_state(edev, id, cable_state);
+ if (ret < 0)
+ return ret;
+
+ return extcon_sync(edev, id);
+}
+EXPORT_SYMBOL_GPL(extcon_set_state_sync);
+
+/**
+ * extcon_get_property() - Get the property value of a specific cable.
+ * @edev: the extcon device that has the cable.
+ * @id: the unique id of each external connector
+ * in extcon enumeration.
+ * @prop: the property id among enum extcon_property.
+ * @prop_val: the pointer which store the value of property.
+ *
+ * When getting the property value of external connector, the external connector
+ * should be attached. If detached state, function just return 0 without
+ * property value. Also, the each property should be included in the list of
+ * supported properties according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_get_property(struct extcon_dev *edev, unsigned int id,
+ unsigned int prop,
+ union extcon_property_value *prop_val)
+{
+ struct extcon_cable *cable;
+ unsigned long flags;
+ int index, ret = 0;
+
+ *prop_val = (union extcon_property_value)(0);
+
+ if (!edev)
+ return -EINVAL;
+
+ /* Check whether the property is supported or not */
+ if (!is_extcon_property_supported(id, prop))
+ return -EINVAL;
+
+ /* Find the cable index of external connector by using id */
+ index = find_cable_index_by_id(edev, id);
+ if (index < 0)
+ return index;
+
+ spin_lock_irqsave(&edev->lock, flags);
+
+ /* Check whether the property is available or not. */
+ if (!is_extcon_property_capability(edev, id, index, prop)) {
+ spin_unlock_irqrestore(&edev->lock, flags);
+ return -EPERM;
+ }
+
+ /*
+ * Check whether the external connector is attached.
+ * If external connector is detached, the user can not
+ * get the property value.
+ */
+ if (!is_extcon_attached(edev, index)) {
+ spin_unlock_irqrestore(&edev->lock, flags);
+ return 0;
+ }
+
+ cable = &edev->cables[index];
+
+ /* Get the property value according to extcon type */
+ switch (prop) {
+ case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+ *prop_val = cable->usb_propval[prop - EXTCON_PROP_USB_MIN];
+ break;
+ case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+ *prop_val = cable->chg_propval[prop - EXTCON_PROP_CHG_MIN];
+ break;
+ case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+ *prop_val = cable->jack_propval[prop - EXTCON_PROP_JACK_MIN];
+ break;
+ case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+ *prop_val = cable->disp_propval[prop - EXTCON_PROP_DISP_MIN];
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock_irqrestore(&edev->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_get_property);
+
+/**
+ * extcon_set_property() - Set the property value of a specific cable.
+ * @edev: the extcon device that has the cable.
+ * @id: the unique id of each external connector
+ * in extcon enumeration.
+ * @prop: the property id among enum extcon_property.
+ * @prop_val: the pointer including the new value of property.
+ *
+ * The each property should be included in the list of supported properties
+ * according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property(struct extcon_dev *edev, unsigned int id,
+ unsigned int prop,
+ union extcon_property_value prop_val)
+{
+ struct extcon_cable *cable;
+ unsigned long flags;
+ int index, ret = 0;
+
+ if (!edev)
+ return -EINVAL;
+
+ /* Check whether the property is supported or not */
+ if (!is_extcon_property_supported(id, prop))
+ return -EINVAL;
+
+ /* Find the cable index of external connector by using id */
+ index = find_cable_index_by_id(edev, id);
+ if (index < 0)
+ return index;
+
+ spin_lock_irqsave(&edev->lock, flags);
+
+ /* Check whether the property is available or not. */
+ if (!is_extcon_property_capability(edev, id, index, prop)) {
+ spin_unlock_irqrestore(&edev->lock, flags);
+ return -EPERM;
+ }
+
+ cable = &edev->cables[index];
+
+ /* Set the property value according to extcon type */
+ switch (prop) {
+ case EXTCON_PROP_USB_MIN ... EXTCON_PROP_USB_MAX:
+ cable->usb_propval[prop - EXTCON_PROP_USB_MIN] = prop_val;
+ break;
+ case EXTCON_PROP_CHG_MIN ... EXTCON_PROP_CHG_MAX:
+ cable->chg_propval[prop - EXTCON_PROP_CHG_MIN] = prop_val;
+ break;
+ case EXTCON_PROP_JACK_MIN ... EXTCON_PROP_JACK_MAX:
+ cable->jack_propval[prop - EXTCON_PROP_JACK_MIN] = prop_val;
+ break;
+ case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
+ cable->disp_propval[prop - EXTCON_PROP_DISP_MIN] = prop_val;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ spin_unlock_irqrestore(&edev->lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(extcon_set_property);
+
+/**
+ * extcon_set_property_sync() - Set the property value of a specific cable
+ with a notification.
+ * @prop_val: the pointer including the new value of property.
+ *
+ * When setting the property value of external connector, the external connector
+ * should be attached. The each property should be included in the list of
+ * supported properties according to the type of external connectors.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id,
+ unsigned int prop,
+ union extcon_property_value prop_val)
+{
+ int ret;
+
+ ret = extcon_set_property(edev, id, prop, prop_val);
+ if (ret < 0)
+ return ret;
+
+ return extcon_sync(edev, id);
+}
+EXPORT_SYMBOL_GPL(extcon_set_property_sync);
+
+/**
+ * extcon_get_property_capability() - Get the capability of property
+ * of an external connector.
+ * @edev: the extcon device that has the cable.
+ * @id: the unique id of each external connector
+ * in extcon enumeration.
+ * @prop: the property id among enum extcon_property.
+ *
+ * Returns 1 if the property is available or 0 if not available.
+ */
+int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id,
+ unsigned int prop)
+{
int index;
if (!edev)
return -EINVAL;
+ /* Check whether the property is supported or not */
+ if (!is_extcon_property_supported(id, prop))
+ return -EINVAL;
+
+ /* Find the cable index of external connector by using id */
index = find_cable_index_by_id(edev, id);
if (index < 0)
return index;
- if (edev->max_supported && edev->max_supported <= index)
+ return is_extcon_property_capability(edev, id, index, prop);
+}
+EXPORT_SYMBOL_GPL(extcon_get_property_capability);
+
+/**
+ * extcon_set_property_capability() - Set the capability of a property
+ * of an external connector.
+ * @edev: the extcon device that has the cable.
+ * @id: the unique id of each external connector
+ * in extcon enumeration.
+ * @prop: the property id among enum extcon_property.
+ *
+ * This function set the capability of a property for an external connector
+ * to mark the bit in capability bitmap which mean the available state of
+ * a property.
+ *
+ * Returns 0 if success or error number if fail
+ */
+int extcon_set_property_capability(struct extcon_dev *edev, unsigned int id,
+ unsigned int prop)
+{
+ struct extcon_cable *cable;
+ int index, type, ret = 0;
+
+ if (!edev)
return -EINVAL;
- state = cable_state ? (1 << index) : 0;
- return extcon_update_state(edev, 1 << index, state);
+ /* Check whether the property is supported or not. */
+ if (!is_extcon_property_supported(id, prop))
+ return -EINVAL;
+
+ /* Find the cable index of external connector by using id. */
+ index = find_cable_index_by_id(edev, id);
+ if (index < 0)
+ return index;
+
+ type = get_extcon_type(prop);
+ if (type < 0)
+ return type;
+
+ cable = &edev->cables[index];
+
+ switch (type) {
+ case EXTCON_TYPE_USB:
+ __set_bit(prop - EXTCON_PROP_USB_MIN, cable->usb_bits);
+ break;
+ case EXTCON_TYPE_CHG:
+ __set_bit(prop - EXTCON_PROP_CHG_MIN, cable->chg_bits);
+ break;
+ case EXTCON_TYPE_JACK:
+ __set_bit(prop - EXTCON_PROP_JACK_MIN, cable->jack_bits);
+ break;
+ case EXTCON_TYPE_DISP:
+ __set_bit(prop - EXTCON_PROP_DISP_MIN, cable->disp_bits);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
+EXPORT_SYMBOL_GPL(extcon_set_property_capability);
/**
* extcon_get_extcon_dev() - Get the extcon device instance from the name
@@ -428,7 +904,7 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
struct notifier_block *nb)
{
unsigned long flags;
- int ret, idx;
+ int ret, idx = -EINVAL;
if (!nb)
return -EINVAL;
@@ -846,13 +1322,13 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index)
return ERR_PTR(-EINVAL);
if (!dev->of_node) {
- dev_err(dev, "device does not have a device node entry\n");
+ dev_dbg(dev, "device does not have a device node entry\n");
return ERR_PTR(-EINVAL);
}
node = of_parse_phandle(dev->of_node, "extcon", index);
if (!node) {
- dev_err(dev, "failed to get phandle in %s node\n",
+ dev_dbg(dev, "failed to get phandle in %s node\n",
dev->of_node->full_name);
return ERR_PTR(-ENODEV);
}
diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c
index 829eec8..2fe1a13 100644
--- a/drivers/firmware/dcdbas.c
+++ b/drivers/firmware/dcdbas.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
+#include <linux/cpu.h>
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -238,33 +239,14 @@ static ssize_t host_control_on_shutdown_store(struct device *dev,
return count;
}
-/**
- * dcdbas_smi_request: generate SMI request
- *
- * Called with smi_data_lock.
- */
-int dcdbas_smi_request(struct smi_cmd *smi_cmd)
+static int raise_smi(void *par)
{
- cpumask_var_t old_mask;
- int ret = 0;
-
- if (smi_cmd->magic != SMI_CMD_MAGIC) {
- dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n",
- __func__);
- return -EBADR;
- }
+ struct smi_cmd *smi_cmd = par;
- /* SMI requires CPU 0 */
- if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
- return -ENOMEM;
-
- cpumask_copy(old_mask, &current->cpus_allowed);
- set_cpus_allowed_ptr(current, cpumask_of(0));
if (smp_processor_id() != 0) {
dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n",
__func__);
- ret = -EBUSY;
- goto out;
+ return -EBUSY;
}
/* generate SMI */
@@ -280,9 +262,28 @@ int dcdbas_smi_request(struct smi_cmd *smi_cmd)
: "memory"
);
-out:
- set_cpus_allowed_ptr(current, old_mask);
- free_cpumask_var(old_mask);
+ return 0;
+}
+/**
+ * dcdbas_smi_request: generate SMI request
+ *
+ * Called with smi_data_lock.
+ */
+int dcdbas_smi_request(struct smi_cmd *smi_cmd)
+{
+ int ret;
+
+ if (smi_cmd->magic != SMI_CMD_MAGIC) {
+ dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n",
+ __func__);
+ return -EBADR;
+ }
+
+ /* SMI requires CPU 0 */
+ get_online_cpus();
+ ret = smp_call_on_cpu(0, raise_smi, smi_cmd, true);
+ put_online_cpus();
+
return ret;
}
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 6394152..c981be1 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -112,6 +112,23 @@ config EFI_CAPSULE_LOADER
Most users should say N.
+config EFI_TEST
+ tristate "EFI Runtime Service Tests Support"
+ depends on EFI
+ default n
+ help
+ This driver uses the efi.<service> function pointers directly instead
+ of going through the efivar API, because it is not trying to test the
+ kernel subsystem, just for testing the UEFI runtime service
+ interfaces which are provided by the firmware. This driver is used
+ by the Firmware Test Suite (FWTS) for testing the UEFI runtime
+ interfaces readiness of the firmware.
+ Details for FWTS are available from:
+ <https://wiki.ubuntu.com/FirmwareTestSuite>
+
+ Say Y here to enable the runtime services support via /dev/efi_test.
+ If unsure, say N.
+
endmenu
config UEFI_CPER
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index a219640..c8a439f 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -10,7 +10,7 @@
KASAN_SANITIZE_runtime-wrappers.o := n
obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o
-obj-$(CONFIG_EFI) += capsule.o
+obj-$(CONFIG_EFI) += capsule.o memmap.o
obj-$(CONFIG_EFI_VARS) += efivars.o
obj-$(CONFIG_EFI_ESRT) += esrt.o
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
@@ -20,6 +20,7 @@ obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
obj-$(CONFIG_EFI_STUB) += libstub/
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
+obj-$(CONFIG_EFI_TEST) += test/
arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
obj-$(CONFIG_ARM) += $(arm-obj-y)
diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c
index c49d50e..8efe130 100644
--- a/drivers/firmware/efi/arm-init.c
+++ b/drivers/firmware/efi/arm-init.c
@@ -26,9 +26,9 @@
u64 efi_system_table;
-static int __init is_normal_ram(efi_memory_desc_t *md)
+static int __init is_memory(efi_memory_desc_t *md)
{
- if (md->attribute & EFI_MEMORY_WB)
+ if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC))
return 1;
return 0;
}
@@ -152,9 +152,9 @@ out:
}
/*
- * Return true for RAM regions we want to permanently reserve.
+ * Return true for regions that can be used as System RAM.
*/
-static __init int is_reserve_region(efi_memory_desc_t *md)
+static __init int is_usable_memory(efi_memory_desc_t *md)
{
switch (md->type) {
case EFI_LOADER_CODE:
@@ -163,18 +163,22 @@ static __init int is_reserve_region(efi_memory_desc_t *md)
case EFI_BOOT_SERVICES_DATA:
case EFI_CONVENTIONAL_MEMORY:
case EFI_PERSISTENT_MEMORY:
- return 0;
+ /*
+ * According to the spec, these regions are no longer reserved
+ * after calling ExitBootServices(). However, we can only use
+ * them as System RAM if they can be mapped writeback cacheable.
+ */
+ return (md->attribute & EFI_MEMORY_WB);
default:
break;
}
- return is_normal_ram(md);
+ return false;
}
static __init void reserve_regions(void)
{
efi_memory_desc_t *md;
u64 paddr, npages, size;
- int resv;
if (efi_enabled(EFI_DBG))
pr_info("Processing EFI memory map:\n");
@@ -191,32 +195,29 @@ static __init void reserve_regions(void)
paddr = md->phys_addr;
npages = md->num_pages;
- resv = is_reserve_region(md);
if (efi_enabled(EFI_DBG)) {
char buf[64];
- pr_info(" 0x%012llx-0x%012llx %s%s\n",
+ pr_info(" 0x%012llx-0x%012llx %s\n",
paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
- efi_md_typeattr_format(buf, sizeof(buf), md),
- resv ? "*" : "");
+ efi_md_typeattr_format(buf, sizeof(buf), md));
}
memrange_efi_to_native(&paddr, &npages);
size = npages << PAGE_SHIFT;
- if (is_normal_ram(md))
+ if (is_memory(md)) {
early_init_dt_add_memory_arch(paddr, size);
- if (resv)
- memblock_mark_nomap(paddr, size);
-
+ if (!is_usable_memory(md))
+ memblock_mark_nomap(paddr, size);
+ }
}
-
- set_bit(EFI_MEMMAP, &efi.flags);
}
void __init efi_init(void)
{
+ struct efi_memory_map_data data;
struct efi_fdt_params params;
/* Grab UEFI information placed in FDT by stub */
@@ -225,9 +226,12 @@ void __init efi_init(void)
efi_system_table = params.system_table;
- efi.memmap.phys_map = params.mmap;
- efi.memmap.map = early_memremap_ro(params.mmap, params.mmap_size);
- if (efi.memmap.map == NULL) {
+ data.desc_version = params.desc_ver;
+ data.desc_size = params.desc_size;
+ data.size = params.mmap_size;
+ data.phys_map = params.mmap;
+
+ if (efi_memmap_init_early(&data) < 0) {
/*
* If we are booting via UEFI, the UEFI memory map is the only
* description of memory we have, so there is little point in
@@ -235,9 +239,6 @@ void __init efi_init(void)
*/
panic("Unable to map EFI memory map.\n");
}
- efi.memmap.map_end = efi.memmap.map + params.mmap_size;
- efi.memmap.desc_size = params.desc_size;
- efi.memmap.desc_version = params.desc_ver;
WARN(efi.memmap.desc_version != 1,
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
@@ -248,7 +249,8 @@ void __init efi_init(void)
reserve_regions();
efi_memattr_init();
- early_memunmap(efi.memmap.map, params.mmap_size);
+ efi_esrt_init();
+ efi_memmap_unmap();
memblock_reserve(params.mmap & PAGE_MASK,
PAGE_ALIGN(params.mmap_size +
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
index c394b81..7c75a8d 100644
--- a/drivers/firmware/efi/arm-runtime.c
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -39,6 +39,26 @@ static struct mm_struct efi_mm = {
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
};
+#ifdef CONFIG_ARM64_PTDUMP
+#include <asm/ptdump.h>
+
+static struct ptdump_info efi_ptdump_info = {
+ .mm = &efi_mm,
+ .markers = (struct addr_marker[]){
+ { 0, "UEFI runtime start" },
+ { TASK_SIZE_64, "UEFI runtime end" }
+ },
+ .base_addr = 0,
+};
+
+static int __init ptdump_init(void)
+{
+ return ptdump_register(&efi_ptdump_info, "efi_page_tables");
+}
+device_initcall(ptdump_init);
+
+#endif
+
static bool __init efi_virtmap_init(void)
{
efi_memory_desc_t *md;
@@ -114,14 +134,12 @@ static int __init arm_enable_runtime_services(void)
pr_info("Remapping and enabling EFI services.\n");
- mapsize = efi.memmap.map_end - efi.memmap.map;
+ mapsize = efi.memmap.desc_size * efi.memmap.nr_map;
- efi.memmap.map = memremap(efi.memmap.phys_map, mapsize, MEMREMAP_WB);
- if (!efi.memmap.map) {
+ if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) {
pr_err("Failed to remap EFI memory map\n");
return -ENOMEM;
}
- efi.memmap.map_end = efi.memmap.map + mapsize;
if (!efi_virtmap_init()) {
pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n");
diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
index 30a24d0..1c33d74 100644
--- a/drivers/firmware/efi/efi-pstore.c
+++ b/drivers/firmware/efi/efi-pstore.c
@@ -125,16 +125,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
* @entry: deleting entry
* @turn_off_scanning: Check if a scanning flag should be turned off
*/
-static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
+static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
bool turn_off_scanning)
{
if (entry->deleting) {
list_del(&entry->list);
efivar_entry_iter_end();
efivar_unregister(entry);
- efivar_entry_iter_begin();
+ if (efivar_entry_iter_begin())
+ return -EINTR;
} else if (turn_off_scanning)
entry->scanning = false;
+
+ return 0;
}
/**
@@ -144,13 +147,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
* @head: list head
* @stop: a flag checking if scanning will stop
*/
-static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
+static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
struct efivar_entry *next,
struct list_head *head, bool stop)
{
- __efi_pstore_scan_sysfs_exit(pos, true);
+ int ret = __efi_pstore_scan_sysfs_exit(pos, true);
+
+ if (ret)
+ return ret;
+
if (stop)
- __efi_pstore_scan_sysfs_exit(next, &next->list != head);
+ ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head);
+ return ret;
}
/**
@@ -172,13 +180,17 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
struct efivar_entry *entry, *n;
struct list_head *head = &efivar_sysfs_list;
int size = 0;
+ int ret;
if (!*pos) {
list_for_each_entry_safe(entry, n, head, list) {
efi_pstore_scan_sysfs_enter(entry, n, head);
size = efi_pstore_read_func(entry, data);
- efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
+ ret = efi_pstore_scan_sysfs_exit(entry, n, head,
+ size < 0);
+ if (ret)
+ return ret;
if (size)
break;
}
@@ -190,7 +202,9 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
efi_pstore_scan_sysfs_enter((*pos), n, head);
size = efi_pstore_read_func((*pos), data);
- efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
+ ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
+ if (ret)
+ return ret;
if (size)
break;
}
@@ -232,7 +246,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
if (!*data.buf)
return -ENOMEM;
- efivar_entry_iter_begin();
+ if (efivar_entry_iter_begin()) {
+ kfree(*data.buf);
+ return -EINTR;
+ }
size = efi_pstore_sysfs_entry_iter(&data,
(struct efivar_entry **)&psi->data);
efivar_entry_iter_end();
@@ -347,7 +364,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
edata.time = time;
edata.name = efi_name;
- efivar_entry_iter_begin();
+ if (efivar_entry_iter_begin())
+ return -EINTR;
found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
if (found && !entry->scanning) {
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 7dd2e2d..1ac199c 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/ucs2_string.h>
+#include <linux/memblock.h>
#include <asm/early_ioremap.h>
@@ -347,56 +348,31 @@ subsys_initcall(efisubsys_init);
/*
* Find the efi memory descriptor for a given physical address. Given a
- * physicall address, determine if it exists within an EFI Memory Map entry,
+ * physical address, determine if it exists within an EFI Memory Map entry,
* and if so, populate the supplied memory descriptor with the appropriate
* data.
*/
int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
{
- struct efi_memory_map *map = &efi.memmap;
- phys_addr_t p, e;
+ efi_memory_desc_t *md;
if (!efi_enabled(EFI_MEMMAP)) {
pr_err_once("EFI_MEMMAP is not enabled.\n");
return -EINVAL;
}
- if (!map) {
- pr_err_once("efi.memmap is not set.\n");
- return -EINVAL;
- }
if (!out_md) {
pr_err_once("out_md is null.\n");
return -EINVAL;
}
- if (WARN_ON_ONCE(!map->phys_map))
- return -EINVAL;
- if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0))
- return -EINVAL;
- e = map->phys_map + map->nr_map * map->desc_size;
- for (p = map->phys_map; p < e; p += map->desc_size) {
- efi_memory_desc_t *md;
+ for_each_efi_memory_desc(md) {
u64 size;
u64 end;
- /*
- * If a driver calls this after efi_free_boot_services,
- * ->map will be NULL, and the target may also not be mapped.
- * So just always get our own virtual map on the CPU.
- *
- */
- md = early_memremap(p, sizeof (*md));
- if (!md) {
- pr_err_once("early_memremap(%pa, %zu) failed.\n",
- &p, sizeof (*md));
- return -ENOMEM;
- }
-
if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
md->type != EFI_BOOT_SERVICES_DATA &&
md->type != EFI_RUNTIME_SERVICES_DATA) {
- early_memunmap(md, sizeof (*md));
continue;
}
@@ -404,11 +380,8 @@ int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
end = md->phys_addr + size;
if (phys_addr >= md->phys_addr && phys_addr < end) {
memcpy(out_md, md, sizeof(*out_md));
- early_memunmap(md, sizeof (*md));
return 0;
}
-
- early_memunmap(md, sizeof (*md));
}
pr_err_once("requested map not found.\n");
return -ENOENT;
@@ -424,6 +397,35 @@ u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
return end;
}
+void __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {}
+
+/**
+ * efi_mem_reserve - Reserve an EFI memory region
+ * @addr: Physical address to reserve
+ * @size: Size of reservation
+ *
+ * Mark a region as reserved from general kernel allocation and
+ * prevent it being released by efi_free_boot_services().
+ *
+ * This function should be called drivers once they've parsed EFI
+ * configuration tables to figure out where their data lives, e.g.
+ * efi_esrt_init().
+ */
+void __init efi_mem_reserve(phys_addr_t addr, u64 size)
+{
+ if (!memblock_is_region_reserved(addr, size))
+ memblock_reserve(addr, size);
+
+ /*
+ * Some architectures (x86) reserve all boot services ranges
+ * until efi_free_boot_services() because of buggy firmware
+ * implementations. This means the above memblock_reserve() is
+ * superfluous on x86 and instead what it needs to do is
+ * ensure the @start, @size is not freed.
+ */
+ efi_arch_mem_reserve(addr, size);
+}
+
static __initdata efi_config_table_type_t common_tables[] = {
{ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20},
{ACPI_TABLE_GUID, "ACPI", &efi.acpi},
@@ -811,6 +813,9 @@ int efi_status_to_err(efi_status_t status)
case EFI_NOT_FOUND:
err = -ENOENT;
break;
+ case EFI_ABORTED:
+ err = -EINTR;
+ break;
default:
err = -EINVAL;
}
diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c
index 116b244..3e626fd 100644
--- a/drivers/firmware/efi/efivars.c
+++ b/drivers/firmware/efi/efivars.c
@@ -510,7 +510,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
vendor = del_var->VendorGuid;
}
- efivar_entry_iter_begin();
+ if (efivar_entry_iter_begin())
+ return -EINTR;
entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true);
if (!entry)
err = -EINVAL;
@@ -575,7 +576,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
return ret;
kobject_uevent(&new_var->kobj, KOBJ_ADD);
- efivar_entry_add(new_var, &efivar_sysfs_list);
+ if (efivar_entry_add(new_var, &efivar_sysfs_list)) {
+ efivar_unregister(new_var);
+ return -EINTR;
+ }
return 0;
}
@@ -690,7 +694,10 @@ static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor,
static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
{
- efivar_entry_remove(entry);
+ int err = efivar_entry_remove(entry);
+
+ if (err)
+ return err;
efivar_unregister(entry);
return 0;
}
@@ -698,7 +705,14 @@ static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
static void efivars_sysfs_exit(void)
{
/* Remove all entries and destroy */
- __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, NULL, NULL);
+ int err;
+
+ err = __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list,
+ NULL, NULL);
+ if (err) {
+ pr_err("efivars: Failed to destroy sysfs entries\n");
+ return;
+ }
if (efivars_new_var)
sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var);
diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c
index 75feb3f..1491407 100644
--- a/drivers/firmware/efi/esrt.c
+++ b/drivers/firmware/efi/esrt.c
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/efi.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/list.h>
@@ -235,7 +236,7 @@ static struct attribute_group esrt_attr_group = {
};
/*
- * remap the table, copy it to kmalloced pages, and unmap it.
+ * remap the table, validate it, mark it reserved and unmap it.
*/
void __init efi_esrt_init(void)
{
@@ -335,7 +336,7 @@ void __init efi_esrt_init(void)
end = esrt_data + size;
pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
- memblock_reserve(esrt_data, esrt_data_size);
+ efi_mem_reserve(esrt_data, esrt_data_size);
pr_debug("esrt-init: loaded.\n");
err_memunmap:
@@ -382,28 +383,18 @@ static void cleanup_entry_list(void)
static int __init esrt_sysfs_init(void)
{
int error;
- struct efi_system_resource_table __iomem *ioesrt;
pr_debug("esrt-sysfs: loading.\n");
if (!esrt_data || !esrt_data_size)
return -ENOSYS;
- ioesrt = ioremap(esrt_data, esrt_data_size);
- if (!ioesrt) {
- pr_err("ioremap(%pa, %zu) failed.\n", &esrt_data,
- esrt_data_size);
- return -ENOMEM;
- }
-
- esrt = kmalloc(esrt_data_size, GFP_KERNEL);
+ esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
if (!esrt) {
- pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size);
- iounmap(ioesrt);
+ pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
+ esrt_data_size);
return -ENOMEM;
}
- memcpy_fromio(esrt, ioesrt, esrt_data_size);
-
esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
if (!esrt_kobj) {
pr_err("Firmware table registration failed.\n");
@@ -429,8 +420,6 @@ static int __init esrt_sysfs_init(void)
if (error)
goto err_cleanup_list;
- memblock_remove(esrt_data, esrt_data_size);
-
pr_debug("esrt-sysfs: loaded.\n");
return 0;
diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c
index 48430ab..520a40e 100644
--- a/drivers/firmware/efi/fake_mem.c
+++ b/drivers/firmware/efi/fake_mem.c
@@ -35,17 +35,13 @@
#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
-struct fake_mem {
- struct range range;
- u64 attribute;
-};
-static struct fake_mem fake_mems[EFI_MAX_FAKEMEM];
+static struct efi_mem_range fake_mems[EFI_MAX_FAKEMEM];
static int nr_fake_mem;
static int __init cmp_fake_mem(const void *x1, const void *x2)
{
- const struct fake_mem *m1 = x1;
- const struct fake_mem *m2 = x2;
+ const struct efi_mem_range *m1 = x1;
+ const struct efi_mem_range *m2 = x2;
if (m1->range.start < m2->range.start)
return -1;
@@ -56,40 +52,21 @@ static int __init cmp_fake_mem(const void *x1, const void *x2)
void __init efi_fake_memmap(void)
{
- u64 start, end, m_start, m_end, m_attr;
int new_nr_map = efi.memmap.nr_map;
efi_memory_desc_t *md;
phys_addr_t new_memmap_phy;
void *new_memmap;
- void *old, *new;
int i;
- if (!nr_fake_mem || !efi_enabled(EFI_MEMMAP))
+ if (!nr_fake_mem)
return;
/* count up the number of EFI memory descriptor */
- for_each_efi_memory_desc(md) {
- start = md->phys_addr;
- end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
-
- for (i = 0; i < nr_fake_mem; i++) {
- /* modifying range */
- m_start = fake_mems[i].range.start;
- m_end = fake_mems[i].range.end;
-
- if (m_start <= start) {
- /* split into 2 parts */
- if (start < m_end && m_end < end)
- new_nr_map++;
- }
- if (start < m_start && m_start < end) {
- /* split into 3 parts */
- if (m_end < end)
- new_nr_map += 2;
- /* split into 2 parts */
- if (end <= m_end)
- new_nr_map++;
- }
+ for (i = 0; i < nr_fake_mem; i++) {
+ for_each_efi_memory_desc(md) {
+ struct range *r = &fake_mems[i].range;
+
+ new_nr_map += efi_memmap_split_count(md, r);
}
}
@@ -107,85 +84,13 @@ void __init efi_fake_memmap(void)
return;
}
- for (old = efi.memmap.map, new = new_memmap;
- old < efi.memmap.map_end;
- old += efi.memmap.desc_size, new += efi.memmap.desc_size) {
-
- /* copy original EFI memory descriptor */
- memcpy(new, old, efi.memmap.desc_size);
- md = new;
- start = md->phys_addr;
- end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
-
- for (i = 0; i < nr_fake_mem; i++) {
- /* modifying range */
- m_start = fake_mems[i].range.start;
- m_end = fake_mems[i].range.end;
- m_attr = fake_mems[i].attribute;
-
- if (m_start <= start && end <= m_end)
- md->attribute |= m_attr;
-
- if (m_start <= start &&
- (start < m_end && m_end < end)) {
- /* first part */
- md->attribute |= m_attr;
- md->num_pages = (m_end - md->phys_addr + 1) >>
- EFI_PAGE_SHIFT;
- /* latter part */
- new += efi.memmap.desc_size;
- memcpy(new, old, efi.memmap.desc_size);
- md = new;
- md->phys_addr = m_end + 1;
- md->num_pages = (end - md->phys_addr + 1) >>
- EFI_PAGE_SHIFT;
- }
-
- if ((start < m_start && m_start < end) && m_end < end) {
- /* first part */
- md->num_pages = (m_start - md->phys_addr) >>
- EFI_PAGE_SHIFT;
- /* middle part */
- new += efi.memmap.desc_size;
- memcpy(new, old, efi.memmap.desc_size);
- md = new;
- md->attribute |= m_attr;
- md->phys_addr = m_start;
- md->num_pages = (m_end - m_start + 1) >>
- EFI_PAGE_SHIFT;
- /* last part */
- new += efi.memmap.desc_size;
- memcpy(new, old, efi.memmap.desc_size);
- md = new;
- md->phys_addr = m_end + 1;
- md->num_pages = (end - m_end) >>
- EFI_PAGE_SHIFT;
- }
-
- if ((start < m_start && m_start < end) &&
- (end <= m_end)) {
- /* first part */
- md->num_pages = (m_start - md->phys_addr) >>
- EFI_PAGE_SHIFT;
- /* latter part */
- new += efi.memmap.desc_size;
- memcpy(new, old, efi.memmap.desc_size);
- md = new;
- md->phys_addr = m_start;
- md->num_pages = (end - md->phys_addr + 1) >>
- EFI_PAGE_SHIFT;
- md->attribute |= m_attr;
- }
- }
- }
+ for (i = 0; i < nr_fake_mem; i++)
+ efi_memmap_insert(&efi.memmap, new_memmap, &fake_mems[i]);
/* swap into new EFI memmap */
- efi_unmap_memmap();
- efi.memmap.map = new_memmap;
- efi.memmap.phys_map = new_memmap_phy;
- efi.memmap.nr_map = new_nr_map;
- efi.memmap.map_end = efi.memmap.map + efi.memmap.nr_map * efi.memmap.desc_size;
- set_bit(EFI_MEMMAP, &efi.flags);
+ early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map);
+
+ efi_memmap_install(new_memmap_phy, new_nr_map);
/* print new EFI memmap */
efi_print_memmap();
@@ -223,7 +128,7 @@ static int __init setup_fake_mem(char *p)
p++;
}
- sort(fake_mems, nr_fake_mem, sizeof(struct fake_mem),
+ sort(fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
cmp_fake_mem, NULL);
for (i = 0; i < nr_fake_mem; i++)
diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c
new file mode 100644
index 0000000..f03ddec
--- /dev/null
+++ b/drivers/firmware/efi/memmap.c
@@ -0,0 +1,303 @@
+/*
+ * Common EFI memory map functions.
+ */
+
+#define pr_fmt(fmt) "efi: " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/efi.h>
+#include <linux/io.h>
+#include <asm/early_ioremap.h>
+
+/**
+ * __efi_memmap_init - Common code for mapping the EFI memory map
+ * @data: EFI memory map data
+ * @late: Use early or late mapping function?
+ *
+ * This function takes care of figuring out which function to use to
+ * map the EFI memory map in efi.memmap based on how far into the boot
+ * we are.
+ *
+ * During bootup @late should be %false since we only have access to
+ * the early_memremap*() functions as the vmalloc space isn't setup.
+ * Once the kernel is fully booted we can fallback to the more robust
+ * memremap*() API.
+ *
+ * Returns zero on success, a negative error code on failure.
+ */
+static int __init
+__efi_memmap_init(struct efi_memory_map_data *data, bool late)
+{
+ struct efi_memory_map map;
+ phys_addr_t phys_map;
+
+ if (efi_enabled(EFI_PARAVIRT))
+ return 0;
+
+ phys_map = data->phys_map;
+
+ if (late)
+ map.map = memremap(phys_map, data->size, MEMREMAP_WB);
+ else
+ map.map = early_memremap(phys_map, data->size);
+
+ if (!map.map) {
+ pr_err("Could not map the memory map!\n");
+ return -ENOMEM;
+ }
+
+ map.phys_map = data->phys_map;
+ map.nr_map = data->size / data->desc_size;
+ map.map_end = map.map + data->size;
+
+ map.desc_version = data->desc_version;
+ map.desc_size = data->desc_size;
+ map.late = late;
+
+ set_bit(EFI_MEMMAP, &efi.flags);
+
+ efi.memmap = map;
+
+ return 0;
+}
+
+/**
+ * efi_memmap_init_early - Map the EFI memory map data structure
+ * @data: EFI memory map data
+ *
+ * Use early_memremap() to map the passed in EFI memory map and assign
+ * it to efi.memmap.
+ */
+int __init efi_memmap_init_early(struct efi_memory_map_data *data)
+{
+ /* Cannot go backwards */
+ WARN_ON(efi.memmap.late);
+
+ return __efi_memmap_init(data, false);
+}
+
+void __init efi_memmap_unmap(void)
+{
+ if (!efi.memmap.late) {
+ unsigned long size;
+
+ size = efi.memmap.desc_size * efi.memmap.nr_map;
+ early_memunmap(efi.memmap.map, size);
+ } else {
+ memunmap(efi.memmap.map);
+ }
+
+ efi.memmap.map = NULL;
+ clear_bit(EFI_MEMMAP, &efi.flags);
+}
+
+/**
+ * efi_memmap_init_late - Map efi.memmap with memremap()
+ * @phys_addr: Physical address of the new EFI memory map
+ * @size: Size in bytes of the new EFI memory map
+ *
+ * Setup a mapping of the EFI memory map using ioremap_cache(). This
+ * function should only be called once the vmalloc space has been
+ * setup and is therefore not suitable for calling during early EFI
+ * initialise, e.g. in efi_init(). Additionally, it expects
+ * efi_memmap_init_early() to have already been called.
+ *
+ * The reason there are two EFI memmap initialisation
+ * (efi_memmap_init_early() and this late version) is because the
+ * early EFI memmap should be explicitly unmapped once EFI
+ * initialisation is complete as the fixmap space used to map the EFI
+ * memmap (via early_memremap()) is a scarce resource.
+ *
+ * This late mapping is intended to persist for the duration of
+ * runtime so that things like efi_mem_desc_lookup() and
+ * efi_mem_attributes() always work.
+ *
+ * Returns zero on success, a negative error code on failure.
+ */
+int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size)
+{
+ struct efi_memory_map_data data = {
+ .phys_map = addr,
+ .size = size,
+ };
+
+ /* Did we forget to unmap the early EFI memmap? */
+ WARN_ON(efi.memmap.map);
+
+ /* Were we already called? */
+ WARN_ON(efi.memmap.late);
+
+ /*
+ * It makes no sense to allow callers to register different
+ * values for the following fields. Copy them out of the
+ * existing early EFI memmap.
+ */
+ data.desc_version = efi.memmap.desc_version;
+ data.desc_size = efi.memmap.desc_size;
+
+ return __efi_memmap_init(&data, true);
+}
+
+/**
+ * efi_memmap_install - Install a new EFI memory map in efi.memmap
+ * @addr: Physical address of the memory map
+ * @nr_map: Number of entries in the memory map
+ *
+ * Unlike efi_memmap_init_*(), this function does not allow the caller
+ * to switch from early to late mappings. It simply uses the existing
+ * mapping function and installs the new memmap.
+ *
+ * Returns zero on success, a negative error code on failure.
+ */
+int __init efi_memmap_install(phys_addr_t addr, unsigned int nr_map)
+{
+ struct efi_memory_map_data data;
+
+ efi_memmap_unmap();
+
+ data.phys_map = addr;
+ data.size = efi.memmap.desc_size * nr_map;
+ data.desc_version = efi.memmap.desc_version;
+ data.desc_size = efi.memmap.desc_size;
+
+ return __efi_memmap_init(&data, efi.memmap.late);
+}
+
+/**
+ * efi_memmap_split_count - Count number of additional EFI memmap entries
+ * @md: EFI memory descriptor to split
+ * @range: Address range (start, end) to split around
+ *
+ * Returns the number of additional EFI memmap entries required to
+ * accomodate @range.
+ */
+int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range)
+{
+ u64 m_start, m_end;
+ u64 start, end;
+ int count = 0;
+
+ start = md->phys_addr;
+ end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
+
+ /* modifying range */
+ m_start = range->start;
+ m_end = range->end;
+
+ if (m_start <= start) {
+ /* split into 2 parts */
+ if (start < m_end && m_end < end)
+ count++;
+ }
+
+ if (start < m_start && m_start < end) {
+ /* split into 3 parts */
+ if (m_end < end)
+ count += 2;
+ /* split into 2 parts */
+ if (end <= m_end)
+ count++;
+ }
+
+ return count;
+}
+
+/**
+ * efi_memmap_insert - Insert a memory region in an EFI memmap
+ * @old_memmap: The existing EFI memory map structure
+ * @buf: Address of buffer to store new map
+ * @mem: Memory map entry to insert
+ *
+ * It is suggested that you call efi_memmap_split_count() first
+ * to see how large @buf needs to be.
+ */
+void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf,
+ struct efi_mem_range *mem)
+{
+ u64 m_start, m_end, m_attr;
+ efi_memory_desc_t *md;
+ u64 start, end;
+ void *old, *new;
+
+ /* modifying range */
+ m_start = mem->range.start;
+ m_end = mem->range.end;
+ m_attr = mem->attribute;
+
+ /*
+ * The EFI memory map deals with regions in EFI_PAGE_SIZE
+ * units. Ensure that the region described by 'mem' is aligned
+ * correctly.
+ */
+ if (!IS_ALIGNED(m_start, EFI_PAGE_SIZE) ||
+ !IS_ALIGNED(m_end + 1, EFI_PAGE_SIZE)) {
+ WARN_ON(1);
+ return;
+ }
+
+ for (old = old_memmap->map, new = buf;
+ old < old_memmap->map_end;
+ old += old_memmap->desc_size, new += old_memmap->desc_size) {
+
+ /* copy original EFI memory descriptor */
+ memcpy(new, old, old_memmap->desc_size);
+ md = new;
+ start = md->phys_addr;
+ end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
+
+ if (m_start <= start && end <= m_end)
+ md->attribute |= m_attr;
+
+ if (m_start <= start &&
+ (start < m_end && m_end < end)) {
+ /* first part */
+ md->attribute |= m_attr;
+ md->num_pages = (m_end - md->phys_addr + 1) >>
+ EFI_PAGE_SHIFT;
+ /* latter part */
+ new += old_memmap->desc_size;
+ memcpy(new, old, old_memmap->desc_size);
+ md = new;
+ md->phys_addr = m_end + 1;
+ md->num_pages = (end - md->phys_addr + 1) >>
+ EFI_PAGE_SHIFT;
+ }
+
+ if ((start < m_start && m_start < end) && m_end < end) {
+ /* first part */
+ md->num_pages = (m_start - md->phys_addr) >>
+ EFI_PAGE_SHIFT;
+ /* middle part */
+ new += old_memmap->desc_size;
+ memcpy(new, old, old_memmap->desc_size);
+ md = new;
+ md->attribute |= m_attr;
+ md->phys_addr = m_start;
+ md->num_pages = (m_end - m_start + 1) >>
+ EFI_PAGE_SHIFT;
+ /* last part */
+ new += old_memmap->desc_size;
+ memcpy(new, old, old_memmap->desc_size);
+ md = new;
+ md->phys_addr = m_end + 1;
+ md->num_pages = (end - m_end) >>
+ EFI_PAGE_SHIFT;
+ }
+
+ if ((start < m_start && m_start < end) &&
+ (end <= m_end)) {
+ /* first part */
+ md->num_pages = (m_start - md->phys_addr) >>
+ EFI_PAGE_SHIFT;
+ /* latter part */
+ new += old_memmap->desc_size;
+ memcpy(new, old, old_memmap->desc_size);
+ md = new;
+ md->phys_addr = m_start;
+ md->num_pages = (end - md->phys_addr + 1) >>
+ EFI_PAGE_SHIFT;
+ md->attribute |= m_attr;
+ }
+ }
+}
diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c
index 5c55227..8e64b77 100644
--- a/drivers/firmware/efi/runtime-map.c
+++ b/drivers/firmware/efi/runtime-map.c
@@ -14,10 +14,6 @@
#include <asm/setup.h>
-static void *efi_runtime_map;
-static int nr_efi_runtime_map;
-static u32 efi_memdesc_size;
-
struct efi_runtime_map_entry {
efi_memory_desc_t md;
struct kobject kobj; /* kobject for each entry */
@@ -106,7 +102,8 @@ static struct kobj_type __refdata map_ktype = {
static struct kset *map_kset;
static struct efi_runtime_map_entry *
-add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
+add_sysfs_runtime_map_entry(struct kobject *kobj, int nr,
+ efi_memory_desc_t *md)
{
int ret;
struct efi_runtime_map_entry *entry;
@@ -124,8 +121,7 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
return ERR_PTR(-ENOMEM);
}
- memcpy(&entry->md, efi_runtime_map + nr * efi_memdesc_size,
- sizeof(efi_memory_desc_t));
+ memcpy(&entry->md, md, sizeof(efi_memory_desc_t));
kobject_init(&entry->kobj, &map_ktype);
entry->kobj.kset = map_kset;
@@ -142,12 +138,12 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
int efi_get_runtime_map_size(void)
{
- return nr_efi_runtime_map * efi_memdesc_size;
+ return efi.memmap.nr_map * efi.memmap.desc_size;
}
int efi_get_runtime_map_desc_size(void)
{
- return efi_memdesc_size;
+ return efi.memmap.desc_size;
}
int efi_runtime_map_copy(void *buf, size_t bufsz)
@@ -157,38 +153,33 @@ int efi_runtime_map_copy(void *buf, size_t bufsz)
if (sz > bufsz)
sz = bufsz;
- memcpy(buf, efi_runtime_map, sz);
+ memcpy(buf, efi.memmap.map, sz);
return 0;
}
-void efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size)
-{
- efi_runtime_map = map;
- nr_efi_runtime_map = nr_entries;
- efi_memdesc_size = desc_size;
-}
-
int __init efi_runtime_map_init(struct kobject *efi_kobj)
{
int i, j, ret = 0;
struct efi_runtime_map_entry *entry;
+ efi_memory_desc_t *md;
- if (!efi_runtime_map)
+ if (!efi_enabled(EFI_MEMMAP))
return 0;
- map_entries = kzalloc(nr_efi_runtime_map * sizeof(entry), GFP_KERNEL);
+ map_entries = kzalloc(efi.memmap.nr_map * sizeof(entry), GFP_KERNEL);
if (!map_entries) {
ret = -ENOMEM;
goto out;
}
- for (i = 0; i < nr_efi_runtime_map; i++) {
- entry = add_sysfs_runtime_map_entry(efi_kobj, i);
+ i = 0;
+ for_each_efi_memory_desc(md) {
+ entry = add_sysfs_runtime_map_entry(efi_kobj, i, md);
if (IS_ERR(entry)) {
ret = PTR_ERR(entry);
goto out_add_entry;
}
- *(map_entries + i) = entry;
+ *(map_entries + i++) = entry;
}
return 0;
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 4195877..ae54870b 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -14,11 +14,13 @@
* This file is released under the GPLv2.
*/
+#define pr_fmt(fmt) "efi: " fmt
+
#include <linux/bug.h>
#include <linux/efi.h>
#include <linux/irqflags.h>
#include <linux/mutex.h>
-#include <linux/spinlock.h>
+#include <linux/semaphore.h>
#include <linux/stringify.h>
#include <asm/efi.h>
@@ -81,20 +83,21 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call)
* +------------------------------------+-------------------------------+
*
* Due to the fact that the EFI pstore may write to the variable store in
- * interrupt context, we need to use a spinlock for at least the groups that
+ * interrupt context, we need to use a lock for at least the groups that
* contain SetVariable() and QueryVariableInfo(). That leaves little else, as
* none of the remaining functions are actually ever called at runtime.
- * So let's just use a single spinlock to serialize all Runtime Services calls.
+ * So let's just use a single lock to serialize all Runtime Services calls.
*/
-static DEFINE_SPINLOCK(efi_runtime_lock);
+static DEFINE_SEMAPHORE(efi_runtime_lock);
static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
{
efi_status_t status;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(get_time, tm, tc);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -102,9 +105,10 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)
{
efi_status_t status;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(set_time, tm);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -114,9 +118,10 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
{
efi_status_t status;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(get_wakeup_time, enabled, pending, tm);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -124,9 +129,10 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
{
efi_status_t status;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(set_wakeup_time, enabled, tm);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -138,10 +144,11 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,
{
efi_status_t status;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(get_variable, name, vendor, attr, data_size,
data);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -151,9 +158,10 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
{
efi_status_t status;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(get_next_variable, name_size, name, vendor);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -165,10 +173,11 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
{
efi_status_t status;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(set_variable, name, vendor, attr, data_size,
data);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -179,12 +188,12 @@ virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
{
efi_status_t status;
- if (!spin_trylock(&efi_runtime_lock))
+ if (down_trylock(&efi_runtime_lock))
return EFI_NOT_READY;
status = efi_call_virt(set_variable, name, vendor, attr, data_size,
data);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -199,10 +208,11 @@ static efi_status_t virt_efi_query_variable_info(u32 attr,
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
return EFI_UNSUPPORTED;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(query_variable_info, attr, storage_space,
remaining_space, max_variable_size);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -217,12 +227,12 @@ virt_efi_query_variable_info_nonblocking(u32 attr,
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
return EFI_UNSUPPORTED;
- if (!spin_trylock(&efi_runtime_lock))
+ if (down_trylock(&efi_runtime_lock))
return EFI_NOT_READY;
status = efi_call_virt(query_variable_info, attr, storage_space,
remaining_space, max_variable_size);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -230,9 +240,10 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
{
efi_status_t status;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(get_next_high_mono_count, count);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -241,9 +252,13 @@ static void virt_efi_reset_system(int reset_type,
unsigned long data_size,
efi_char16_t *data)
{
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock)) {
+ pr_warn("failed to invoke the reset_system() runtime service:\n"
+ "could not get exclusive access to the firmware\n");
+ return;
+ }
__efi_call_virt(reset_system, reset_type, status, data_size, data);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
}
static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
@@ -255,9 +270,10 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
return EFI_UNSUPPORTED;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(update_capsule, capsules, count, sg_list);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
@@ -271,10 +287,11 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
return EFI_UNSUPPORTED;
- spin_lock(&efi_runtime_lock);
+ if (down_interruptible(&efi_runtime_lock))
+ return EFI_ABORTED;
status = efi_call_virt(query_capsule_caps, capsules, count, max_size,
reset_type);
- spin_unlock(&efi_runtime_lock);
+ up(&efi_runtime_lock);
return status;
}
diff --git a/drivers/firmware/efi/test/Makefile b/drivers/firmware/efi/test/Makefile
new file mode 100644
index 0000000..bcd4577
--- /dev/null
+++ b/drivers/firmware/efi/test/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_EFI_TEST) += efi_test.o
diff --git a/drivers/firmware/efi/test/efi_test.c b/drivers/firmware/efi/test/efi_test.c
new file mode 100644
index 0000000..f61bb52
--- /dev/null
+++ b/drivers/firmware/efi/test/efi_test.c
@@ -0,0 +1,749 @@
+/*
+ * EFI Test Driver for Runtime Services
+ *
+ * Copyright(C) 2012-2016 Canonical Ltd.
+ *
+ * This driver exports EFI runtime services interfaces into userspace, which
+ * allow to use and test UEFI runtime services provided by firmware.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/efi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "efi_test.h"
+
+MODULE_AUTHOR("Ivan Hu <ivan.hu@canonical.com>");
+MODULE_DESCRIPTION("EFI Test Driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Count the bytes in 'str', including the terminating NULL.
+ *
+ * Note this function returns the number of *bytes*, not the number of
+ * ucs2 characters.
+ */
+static inline size_t user_ucs2_strsize(efi_char16_t __user *str)
+{
+ efi_char16_t *s = str, c;
+ size_t len;
+
+ if (!str)
+ return 0;
+
+ /* Include terminating NULL */
+ len = sizeof(efi_char16_t);
+
+ if (get_user(c, s++)) {
+ /* Can't read userspace memory for size */
+ return 0;
+ }
+
+ while (c != 0) {
+ if (get_user(c, s++)) {
+ /* Can't read userspace memory for size */
+ return 0;
+ }
+ len += sizeof(efi_char16_t);
+ }
+ return len;
+}
+
+/*
+ * Allocate a buffer and copy a ucs2 string from user space into it.
+ */
+static inline int
+copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
+ size_t len)
+{
+ efi_char16_t *buf;
+
+ if (!src) {
+ *dst = NULL;
+ return 0;
+ }
+
+ if (!access_ok(VERIFY_READ, src, 1))
+ return -EFAULT;
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf) {
+ *dst = NULL;
+ return -ENOMEM;
+ }
+ *dst = buf;
+
+ if (copy_from_user(*dst, src, len)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*
+ * Count the bytes in 'str', including the terminating NULL.
+ *
+ * Just a wrap for user_ucs2_strsize
+ */
+static inline int
+get_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len)
+{
+ if (!access_ok(VERIFY_READ, src, 1))
+ return -EFAULT;
+
+ *len = user_ucs2_strsize(src);
+ if (*len == 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * Calculate the required buffer allocation size and copy a ucs2 string
+ * from user space into it.
+ *
+ * This function differs from copy_ucs2_from_user_len() because it
+ * calculates the size of the buffer to allocate by taking the length of
+ * the string 'src'.
+ *
+ * If a non-zero value is returned, the caller MUST NOT access 'dst'.
+ *
+ * It is the caller's responsibility to free 'dst'.
+ */
+static inline int
+copy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src)
+{
+ size_t len;
+
+ if (!access_ok(VERIFY_READ, src, 1))
+ return -EFAULT;
+
+ len = user_ucs2_strsize(src);
+ if (len == 0)
+ return -EFAULT;
+ return copy_ucs2_from_user_len(dst, src, len);
+}
+
+/*
+ * Copy a ucs2 string to a user buffer.
+ *
+ * This function is a simple wrapper around copy_to_user() that does
+ * nothing if 'src' is NULL, which is useful for reducing the amount of
+ * NULL checking the caller has to do.
+ *
+ * 'len' specifies the number of bytes to copy.
+ */
+static inline int
+copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
+{
+ if (!src)
+ return 0;
+
+ if (!access_ok(VERIFY_WRITE, dst, 1))
+ return -EFAULT;
+
+ return copy_to_user(dst, src, len);
+}
+
+static long efi_runtime_get_variable(unsigned long arg)
+{
+ struct efi_getvariable __user *getvariable_user;
+ struct efi_getvariable getvariable;
+ unsigned long datasize, prev_datasize, *dz;
+ efi_guid_t vendor_guid, *vd = NULL;
+ efi_status_t status;
+ efi_char16_t *name = NULL;
+ u32 attr, *at;
+ void *data = NULL;
+ int rv = 0;
+
+ getvariable_user = (struct efi_getvariable __user *)arg;
+
+ if (copy_from_user(&getvariable, getvariable_user,
+ sizeof(getvariable)))
+ return -EFAULT;
+ if (getvariable.data_size &&
+ get_user(datasize, getvariable.data_size))
+ return -EFAULT;
+ if (getvariable.vendor_guid) {
+ if (copy_from_user(&vendor_guid, getvariable.vendor_guid,
+ sizeof(vendor_guid)))
+ return -EFAULT;
+ vd = &vendor_guid;
+ }
+
+ if (getvariable.variable_name) {
+ rv = copy_ucs2_from_user(&name, getvariable.variable_name);
+ if (rv)
+ return rv;
+ }
+
+ at = getvariable.attributes ? &attr : NULL;
+ dz = getvariable.data_size ? &datasize : NULL;
+
+ if (getvariable.data_size && getvariable.data) {
+ data = kmalloc(datasize, GFP_KERNEL);
+ if (!data) {
+ kfree(name);
+ return -ENOMEM;
+ }
+ }
+
+ prev_datasize = datasize;
+ status = efi.get_variable(name, vd, at, dz, data);
+ kfree(name);
+
+ if (put_user(status, getvariable.status)) {
+ rv = -EFAULT;
+ goto out;
+ }
+
+ if (status != EFI_SUCCESS) {
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ if (dz && put_user(datasize, getvariable.data_size)) {
+ rv = -EFAULT;
+ goto out;
+ }
+ }
+ rv = -EINVAL;
+ goto out;
+ }
+
+ if (prev_datasize < datasize) {
+ rv = -EINVAL;
+ goto out;
+ }
+
+ if (data) {
+ if (copy_to_user(getvariable.data, data, datasize)) {
+ rv = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (at && put_user(attr, getvariable.attributes)) {
+ rv = -EFAULT;
+ goto out;
+ }
+
+ if (dz && put_user(datasize, getvariable.data_size))
+ rv = -EFAULT;
+
+out:
+ kfree(data);
+ return rv;
+
+}
+
+static long efi_runtime_set_variable(unsigned long arg)
+{
+ struct efi_setvariable __user *setvariable_user;
+ struct efi_setvariable setvariable;
+ efi_guid_t vendor_guid;
+ efi_status_t status;
+ efi_char16_t *name = NULL;
+ void *data;
+ int rv = 0;
+
+ setvariable_user = (struct efi_setvariable __user *)arg;
+
+ if (copy_from_user(&setvariable, setvariable_user, sizeof(setvariable)))
+ return -EFAULT;
+ if (copy_from_user(&vendor_guid, setvariable.vendor_guid,
+ sizeof(vendor_guid)))
+ return -EFAULT;
+
+ if (setvariable.variable_name) {
+ rv = copy_ucs2_from_user(&name, setvariable.variable_name);
+ if (rv)
+ return rv;
+ }
+
+ data = kmalloc(setvariable.data_size, GFP_KERNEL);
+ if (!data) {
+ kfree(name);
+ return -ENOMEM;
+ }
+ if (copy_from_user(data, setvariable.data, setvariable.data_size)) {
+ rv = -EFAULT;
+ goto out;
+ }
+
+ status = efi.set_variable(name, &vendor_guid,
+ setvariable.attributes,
+ setvariable.data_size, data);
+
+ if (put_user(status, setvariable.status)) {
+ rv = -EFAULT;
+ goto out;
+ }
+
+ rv = status == EFI_SUCCESS ? 0 : -EINVAL;
+
+out:
+ kfree(data);
+ kfree(name);
+
+ return rv;
+}
+
+static long efi_runtime_get_time(unsigned long arg)
+{
+ struct efi_gettime __user *gettime_user;
+ struct efi_gettime gettime;
+ efi_status_t status;
+ efi_time_cap_t cap;
+ efi_time_t efi_time;
+
+ gettime_user = (struct efi_gettime __user *)arg;
+ if (copy_from_user(&gettime, gettime_user, sizeof(gettime)))
+ return -EFAULT;
+
+ status = efi.get_time(gettime.time ? &efi_time : NULL,
+ gettime.capabilities ? &cap : NULL);
+
+ if (put_user(status, gettime.status))
+ return -EFAULT;
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ if (gettime.capabilities) {
+ efi_time_cap_t __user *cap_local;
+
+ cap_local = (efi_time_cap_t *)gettime.capabilities;
+ if (put_user(cap.resolution, &(cap_local->resolution)) ||
+ put_user(cap.accuracy, &(cap_local->accuracy)) ||
+ put_user(cap.sets_to_zero, &(cap_local->sets_to_zero)))
+ return -EFAULT;
+ }
+ if (gettime.time) {
+ if (copy_to_user(gettime.time, &efi_time, sizeof(efi_time_t)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static long efi_runtime_set_time(unsigned long arg)
+{
+ struct efi_settime __user *settime_user;
+ struct efi_settime settime;
+ efi_status_t status;
+ efi_time_t efi_time;
+
+ settime_user = (struct efi_settime __user *)arg;
+ if (copy_from_user(&settime, settime_user, sizeof(settime)))
+ return -EFAULT;
+ if (copy_from_user(&efi_time, settime.time,
+ sizeof(efi_time_t)))
+ return -EFAULT;
+ status = efi.set_time(&efi_time);
+
+ if (put_user(status, settime.status))
+ return -EFAULT;
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static long efi_runtime_get_waketime(unsigned long arg)
+{
+ struct efi_getwakeuptime __user *getwakeuptime_user;
+ struct efi_getwakeuptime getwakeuptime;
+ efi_bool_t enabled, pending;
+ efi_status_t status;
+ efi_time_t efi_time;
+
+ getwakeuptime_user = (struct efi_getwakeuptime __user *)arg;
+ if (copy_from_user(&getwakeuptime, getwakeuptime_user,
+ sizeof(getwakeuptime)))
+ return -EFAULT;
+
+ status = efi.get_wakeup_time(
+ getwakeuptime.enabled ? (efi_bool_t *)&enabled : NULL,
+ getwakeuptime.pending ? (efi_bool_t *)&pending : NULL,
+ getwakeuptime.time ? &efi_time : NULL);
+
+ if (put_user(status, getwakeuptime.status))
+ return -EFAULT;
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ if (getwakeuptime.enabled && put_user(enabled,
+ getwakeuptime.enabled))
+ return -EFAULT;
+
+ if (getwakeuptime.time) {
+ if (copy_to_user(getwakeuptime.time, &efi_time,
+ sizeof(efi_time_t)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static long efi_runtime_set_waketime(unsigned long arg)
+{
+ struct efi_setwakeuptime __user *setwakeuptime_user;
+ struct efi_setwakeuptime setwakeuptime;
+ efi_bool_t enabled;
+ efi_status_t status;
+ efi_time_t efi_time;
+
+ setwakeuptime_user = (struct efi_setwakeuptime __user *)arg;
+
+ if (copy_from_user(&setwakeuptime, setwakeuptime_user,
+ sizeof(setwakeuptime)))
+ return -EFAULT;
+
+ enabled = setwakeuptime.enabled;
+ if (setwakeuptime.time) {
+ if (copy_from_user(&efi_time, setwakeuptime.time,
+ sizeof(efi_time_t)))
+ return -EFAULT;
+
+ status = efi.set_wakeup_time(enabled, &efi_time);
+ } else
+ status = efi.set_wakeup_time(enabled, NULL);
+
+ if (put_user(status, setwakeuptime.status))
+ return -EFAULT;
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static long efi_runtime_get_nextvariablename(unsigned long arg)
+{
+ struct efi_getnextvariablename __user *getnextvariablename_user;
+ struct efi_getnextvariablename getnextvariablename;
+ unsigned long name_size, prev_name_size = 0, *ns = NULL;
+ efi_status_t status;
+ efi_guid_t *vd = NULL;
+ efi_guid_t vendor_guid;
+ efi_char16_t *name = NULL;
+ int rv;
+
+ getnextvariablename_user = (struct efi_getnextvariablename __user *)arg;
+
+ if (copy_from_user(&getnextvariablename, getnextvariablename_user,
+ sizeof(getnextvariablename)))
+ return -EFAULT;
+
+ if (getnextvariablename.variable_name_size) {
+ if (get_user(name_size, getnextvariablename.variable_name_size))
+ return -EFAULT;
+ ns = &name_size;
+ prev_name_size = name_size;
+ }
+
+ if (getnextvariablename.vendor_guid) {
+ if (copy_from_user(&vendor_guid,
+ getnextvariablename.vendor_guid,
+ sizeof(vendor_guid)))
+ return -EFAULT;
+ vd = &vendor_guid;
+ }
+
+ if (getnextvariablename.variable_name) {
+ size_t name_string_size = 0;
+
+ rv = get_ucs2_strsize_from_user(
+ getnextvariablename.variable_name,
+ &name_string_size);
+ if (rv)
+ return rv;
+ /*
+ * The name_size may be smaller than the real buffer size where
+ * variable name located in some use cases. The most typical
+ * case is passing a 0 to get the required buffer size for the
+ * 1st time call. So we need to copy the content from user
+ * space for at least the string size of variable name, or else
+ * the name passed to UEFI may not be terminated as we expected.
+ */
+ rv = copy_ucs2_from_user_len(&name,
+ getnextvariablename.variable_name,
+ prev_name_size > name_string_size ?
+ prev_name_size : name_string_size);
+ if (rv)
+ return rv;
+ }
+
+ status = efi.get_next_variable(ns, name, vd);
+
+ if (put_user(status, getnextvariablename.status)) {
+ rv = -EFAULT;
+ goto out;
+ }
+
+ if (status != EFI_SUCCESS) {
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ if (ns && put_user(*ns,
+ getnextvariablename.variable_name_size)) {
+ rv = -EFAULT;
+ goto out;
+ }
+ }
+ rv = -EINVAL;
+ goto out;
+ }
+
+ if (name) {
+ if (copy_ucs2_to_user_len(getnextvariablename.variable_name,
+ name, prev_name_size)) {
+ rv = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (ns) {
+ if (put_user(*ns, getnextvariablename.variable_name_size)) {
+ rv = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (vd) {
+ if (copy_to_user(getnextvariablename.vendor_guid, vd,
+ sizeof(efi_guid_t)))
+ rv = -EFAULT;
+ }
+
+out:
+ kfree(name);
+ return rv;
+}
+
+static long efi_runtime_get_nexthighmonocount(unsigned long arg)
+{
+ struct efi_getnexthighmonotoniccount __user *getnexthighmonocount_user;
+ struct efi_getnexthighmonotoniccount getnexthighmonocount;
+ efi_status_t status;
+ u32 count;
+
+ getnexthighmonocount_user = (struct
+ efi_getnexthighmonotoniccount __user *)arg;
+
+ if (copy_from_user(&getnexthighmonocount,
+ getnexthighmonocount_user,
+ sizeof(getnexthighmonocount)))
+ return -EFAULT;
+
+ status = efi.get_next_high_mono_count(
+ getnexthighmonocount.high_count ? &count : NULL);
+
+ if (put_user(status, getnexthighmonocount.status))
+ return -EFAULT;
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ if (getnexthighmonocount.high_count &&
+ put_user(count, getnexthighmonocount.high_count))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long efi_runtime_query_variableinfo(unsigned long arg)
+{
+ struct efi_queryvariableinfo __user *queryvariableinfo_user;
+ struct efi_queryvariableinfo queryvariableinfo;
+ efi_status_t status;
+ u64 max_storage, remaining, max_size;
+
+ queryvariableinfo_user = (struct efi_queryvariableinfo __user *)arg;
+
+ if (copy_from_user(&queryvariableinfo, queryvariableinfo_user,
+ sizeof(queryvariableinfo)))
+ return -EFAULT;
+
+ status = efi.query_variable_info(queryvariableinfo.attributes,
+ &max_storage, &remaining, &max_size);
+
+ if (put_user(status, queryvariableinfo.status))
+ return -EFAULT;
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ if (put_user(max_storage,
+ queryvariableinfo.maximum_variable_storage_size))
+ return -EFAULT;
+
+ if (put_user(remaining,
+ queryvariableinfo.remaining_variable_storage_size))
+ return -EFAULT;
+
+ if (put_user(max_size, queryvariableinfo.maximum_variable_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long efi_runtime_query_capsulecaps(unsigned long arg)
+{
+ struct efi_querycapsulecapabilities __user *qcaps_user;
+ struct efi_querycapsulecapabilities qcaps;
+ efi_capsule_header_t *capsules;
+ efi_status_t status;
+ u64 max_size;
+ int i, reset_type;
+ int rv = 0;
+
+ qcaps_user = (struct efi_querycapsulecapabilities __user *)arg;
+
+ if (copy_from_user(&qcaps, qcaps_user, sizeof(qcaps)))
+ return -EFAULT;
+
+ capsules = kcalloc(qcaps.capsule_count + 1,
+ sizeof(efi_capsule_header_t), GFP_KERNEL);
+ if (!capsules)
+ return -ENOMEM;
+
+ for (i = 0; i < qcaps.capsule_count; i++) {
+ efi_capsule_header_t *c;
+ /*
+ * We cannot dereference qcaps.capsule_header_array directly to
+ * obtain the address of the capsule as it resides in the
+ * user space
+ */
+ if (get_user(c, qcaps.capsule_header_array + i)) {
+ rv = -EFAULT;
+ goto out;
+ }
+ if (copy_from_user(&capsules[i], c,
+ sizeof(efi_capsule_header_t))) {
+ rv = -EFAULT;
+ goto out;
+ }
+ }
+
+ qcaps.capsule_header_array = &capsules;
+
+ status = efi.query_capsule_caps((efi_capsule_header_t **)
+ qcaps.capsule_header_array,
+ qcaps.capsule_count,
+ &max_size, &reset_type);
+
+ if (put_user(status, qcaps.status)) {
+ rv = -EFAULT;
+ goto out;
+ }
+
+ if (status != EFI_SUCCESS) {
+ rv = -EINVAL;
+ goto out;
+ }
+
+ if (put_user(max_size, qcaps.maximum_capsule_size)) {
+ rv = -EFAULT;
+ goto out;
+ }
+
+ if (put_user(reset_type, qcaps.reset_type))
+ rv = -EFAULT;
+
+out:
+ kfree(capsules);
+ return rv;
+}
+
+static long efi_test_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case EFI_RUNTIME_GET_VARIABLE:
+ return efi_runtime_get_variable(arg);
+
+ case EFI_RUNTIME_SET_VARIABLE:
+ return efi_runtime_set_variable(arg);
+
+ case EFI_RUNTIME_GET_TIME:
+ return efi_runtime_get_time(arg);
+
+ case EFI_RUNTIME_SET_TIME:
+ return efi_runtime_set_time(arg);
+
+ case EFI_RUNTIME_GET_WAKETIME:
+ return efi_runtime_get_waketime(arg);
+
+ case EFI_RUNTIME_SET_WAKETIME:
+ return efi_runtime_set_waketime(arg);
+
+ case EFI_RUNTIME_GET_NEXTVARIABLENAME:
+ return efi_runtime_get_nextvariablename(arg);
+
+ case EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT:
+ return efi_runtime_get_nexthighmonocount(arg);
+
+ case EFI_RUNTIME_QUERY_VARIABLEINFO:
+ return efi_runtime_query_variableinfo(arg);
+
+ case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
+ return efi_runtime_query_capsulecaps(arg);
+ }
+
+ return -ENOTTY;
+}
+
+static int efi_test_open(struct inode *inode, struct file *file)
+{
+ /*
+ * nothing special to do here
+ * We do accept multiple open files at the same time as we
+ * synchronize on the per call operation.
+ */
+ return 0;
+}
+
+static int efi_test_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+/*
+ * The various file operations we support.
+ */
+static const struct file_operations efi_test_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = efi_test_ioctl,
+ .open = efi_test_open,
+ .release = efi_test_close,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice efi_test_dev = {
+ MISC_DYNAMIC_MINOR,
+ "efi_test",
+ &efi_test_fops
+};
+
+static int __init efi_test_init(void)
+{
+ int ret;
+
+ ret = misc_register(&efi_test_dev);
+ if (ret) {
+ pr_err("efi_test: can't misc_register on minor=%d\n",
+ MISC_DYNAMIC_MINOR);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit efi_test_exit(void)
+{
+ misc_deregister(&efi_test_dev);
+}
+
+module_init(efi_test_init);
+module_exit(efi_test_exit);
diff --git a/drivers/firmware/efi/test/efi_test.h b/drivers/firmware/efi/test/efi_test.h
new file mode 100644
index 0000000..a33a6c6
--- /dev/null
+++ b/drivers/firmware/efi/test/efi_test.h
@@ -0,0 +1,110 @@
+/*
+ * EFI Test driver Header
+ *
+ * Copyright(C) 2012-2016 Canonical Ltd.
+ *
+ */
+
+#ifndef _DRIVERS_FIRMWARE_EFI_TEST_H_
+#define _DRIVERS_FIRMWARE_EFI_TEST_H_
+
+#include <linux/efi.h>
+
+struct efi_getvariable {
+ efi_char16_t *variable_name;
+ efi_guid_t *vendor_guid;
+ u32 *attributes;
+ unsigned long *data_size;
+ void *data;
+ efi_status_t *status;
+} __packed;
+
+struct efi_setvariable {
+ efi_char16_t *variable_name;
+ efi_guid_t *vendor_guid;
+ u32 attributes;
+ unsigned long data_size;
+ void *data;
+ efi_status_t *status;
+} __packed;
+
+struct efi_getnextvariablename {
+ unsigned long *variable_name_size;
+ efi_char16_t *variable_name;
+ efi_guid_t *vendor_guid;
+ efi_status_t *status;
+} __packed;
+
+struct efi_queryvariableinfo {
+ u32 attributes;
+ u64 *maximum_variable_storage_size;
+ u64 *remaining_variable_storage_size;
+ u64 *maximum_variable_size;
+ efi_status_t *status;
+} __packed;
+
+struct efi_gettime {
+ efi_time_t *time;
+ efi_time_cap_t *capabilities;
+ efi_status_t *status;
+} __packed;
+
+struct efi_settime {
+ efi_time_t *time;
+ efi_status_t *status;
+} __packed;
+
+struct efi_getwakeuptime {
+ efi_bool_t *enabled;
+ efi_bool_t *pending;
+ efi_time_t *time;
+ efi_status_t *status;
+} __packed;
+
+struct efi_setwakeuptime {
+ efi_bool_t enabled;
+ efi_time_t *time;
+ efi_status_t *status;
+} __packed;
+
+struct efi_getnexthighmonotoniccount {
+ u32 *high_count;
+ efi_status_t *status;
+} __packed;
+
+struct efi_querycapsulecapabilities {
+ efi_capsule_header_t **capsule_header_array;
+ unsigned long capsule_count;
+ u64 *maximum_capsule_size;
+ int *reset_type;
+ efi_status_t *status;
+} __packed;
+
+#define EFI_RUNTIME_GET_VARIABLE \
+ _IOWR('p', 0x01, struct efi_getvariable)
+#define EFI_RUNTIME_SET_VARIABLE \
+ _IOW('p', 0x02, struct efi_setvariable)
+
+#define EFI_RUNTIME_GET_TIME \
+ _IOR('p', 0x03, struct efi_gettime)
+#define EFI_RUNTIME_SET_TIME \
+ _IOW('p', 0x04, struct efi_settime)
+
+#define EFI_RUNTIME_GET_WAKETIME \
+ _IOR('p', 0x05, struct efi_getwakeuptime)
+#define EFI_RUNTIME_SET_WAKETIME \
+ _IOW('p', 0x06, struct efi_setwakeuptime)
+
+#define EFI_RUNTIME_GET_NEXTVARIABLENAME \
+ _IOWR('p', 0x07, struct efi_getnextvariablename)
+
+#define EFI_RUNTIME_QUERY_VARIABLEINFO \
+ _IOR('p', 0x08, struct efi_queryvariableinfo)
+
+#define EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT \
+ _IOR('p', 0x09, struct efi_getnexthighmonotoniccount)
+
+#define EFI_RUNTIME_QUERY_CAPSULECAPABILITIES \
+ _IOR('p', 0x0A, struct efi_querycapsulecapabilities)
+
+#endif /* _DRIVERS_FIRMWARE_EFI_TEST_H_ */
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index d3b7513..9336ffd 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -37,6 +37,14 @@
/* Private pointer to registered efivars */
static struct efivars *__efivars;
+/*
+ * efivars_lock protects three things:
+ * 1) efivarfs_list and efivars_sysfs_list
+ * 2) ->ops calls
+ * 3) (un)registration of __efivars
+ */
+static DEFINE_SEMAPHORE(efivars_lock);
+
static bool efivar_wq_enabled = true;
DECLARE_WORK(efivar_work, NULL);
EXPORT_SYMBOL_GPL(efivar_work);
@@ -434,7 +442,10 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
return -ENOMEM;
}
- spin_lock_irq(&__efivars->lock);
+ if (down_interruptible(&efivars_lock)) {
+ err = -EINTR;
+ goto free;
+ }
/*
* Per EFI spec, the maximum storage allocated for both
@@ -450,7 +461,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
switch (status) {
case EFI_SUCCESS:
if (duplicates)
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
variable_name_size = var_name_strnsize(variable_name,
variable_name_size);
@@ -476,8 +487,12 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
status = EFI_NOT_FOUND;
}
- if (duplicates)
- spin_lock_irq(&__efivars->lock);
+ if (duplicates) {
+ if (down_interruptible(&efivars_lock)) {
+ err = -EINTR;
+ goto free;
+ }
+ }
break;
case EFI_NOT_FOUND:
@@ -491,8 +506,8 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
} while (status != EFI_NOT_FOUND);
- spin_unlock_irq(&__efivars->lock);
-
+ up(&efivars_lock);
+free:
kfree(variable_name);
return err;
@@ -503,24 +518,34 @@ EXPORT_SYMBOL_GPL(efivar_init);
* efivar_entry_add - add entry to variable list
* @entry: entry to add to list
* @head: list head
+ *
+ * Returns 0 on success, or a kernel error code on failure.
*/
-void efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
+int efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
{
- spin_lock_irq(&__efivars->lock);
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
list_add(&entry->list, head);
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(efivar_entry_add);
/**
* efivar_entry_remove - remove entry from variable list
* @entry: entry to remove from list
+ *
+ * Returns 0 on success, or a kernel error code on failure.
*/
-void efivar_entry_remove(struct efivar_entry *entry)
+int efivar_entry_remove(struct efivar_entry *entry)
{
- spin_lock_irq(&__efivars->lock);
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
list_del(&entry->list);
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(efivar_entry_remove);
@@ -537,10 +562,8 @@ EXPORT_SYMBOL_GPL(efivar_entry_remove);
*/
static void efivar_entry_list_del_unlock(struct efivar_entry *entry)
{
- lockdep_assert_held(&__efivars->lock);
-
list_del(&entry->list);
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
}
/**
@@ -563,8 +586,6 @@ int __efivar_entry_delete(struct efivar_entry *entry)
const struct efivar_operations *ops = __efivars->ops;
efi_status_t status;
- lockdep_assert_held(&__efivars->lock);
-
status = ops->set_variable(entry->var.VariableName,
&entry->var.VendorGuid,
0, 0, NULL);
@@ -581,20 +602,22 @@ EXPORT_SYMBOL_GPL(__efivar_entry_delete);
* variable list. It is the caller's responsibility to free @entry
* once we return.
*
- * Returns 0 on success, or a converted EFI status code if
- * set_variable() fails.
+ * Returns 0 on success, -EINTR if we can't grab the semaphore,
+ * converted EFI status code if set_variable() fails.
*/
int efivar_entry_delete(struct efivar_entry *entry)
{
const struct efivar_operations *ops = __efivars->ops;
efi_status_t status;
- spin_lock_irq(&__efivars->lock);
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
+
status = ops->set_variable(entry->var.VariableName,
&entry->var.VendorGuid,
0, 0, NULL);
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
return efi_status_to_err(status);
}
@@ -620,9 +643,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_delete);
* If @head is not NULL a lookup is performed to determine whether
* the entry is already on the list.
*
- * Returns 0 on success, -EEXIST if a lookup is performed and the entry
- * already exists on the list, or a converted EFI status code if
- * set_variable() fails.
+ * Returns 0 on success, -EINTR if we can't grab the semaphore,
+ * -EEXIST if a lookup is performed and the entry already exists on
+ * the list, or a converted EFI status code if set_variable() fails.
*/
int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
unsigned long size, void *data, struct list_head *head)
@@ -632,10 +655,10 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
efi_char16_t *name = entry->var.VariableName;
efi_guid_t vendor = entry->var.VendorGuid;
- spin_lock_irq(&__efivars->lock);
-
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
if (head && efivar_entry_find(name, vendor, head, false)) {
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
return -EEXIST;
}
@@ -644,7 +667,7 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
status = ops->set_variable(name, &vendor,
attributes, size, data);
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
return efi_status_to_err(status);
@@ -658,30 +681,29 @@ EXPORT_SYMBOL_GPL(efivar_entry_set);
* from crash/panic handlers.
*
* Crucially, this function will not block if it cannot acquire
- * __efivars->lock. Instead, it returns -EBUSY.
+ * efivars_lock. Instead, it returns -EBUSY.
*/
static int
efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor,
u32 attributes, unsigned long size, void *data)
{
const struct efivar_operations *ops = __efivars->ops;
- unsigned long flags;
efi_status_t status;
- if (!spin_trylock_irqsave(&__efivars->lock, flags))
+ if (down_trylock(&efivars_lock))
return -EBUSY;
status = check_var_size_nonblocking(attributes,
size + ucs2_strsize(name, 1024));
if (status != EFI_SUCCESS) {
- spin_unlock_irqrestore(&__efivars->lock, flags);
+ up(&efivars_lock);
return -ENOSPC;
}
status = ops->set_variable_nonblocking(name, &vendor, attributes,
size, data);
- spin_unlock_irqrestore(&__efivars->lock, flags);
+ up(&efivars_lock);
return efi_status_to_err(status);
}
@@ -706,7 +728,6 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
bool block, unsigned long size, void *data)
{
const struct efivar_operations *ops = __efivars->ops;
- unsigned long flags;
efi_status_t status;
if (!ops->query_variable_store)
@@ -727,21 +748,22 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
size, data);
if (!block) {
- if (!spin_trylock_irqsave(&__efivars->lock, flags))
+ if (down_trylock(&efivars_lock))
return -EBUSY;
} else {
- spin_lock_irqsave(&__efivars->lock, flags);
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
}
status = check_var_size(attributes, size + ucs2_strsize(name, 1024));
if (status != EFI_SUCCESS) {
- spin_unlock_irqrestore(&__efivars->lock, flags);
+ up(&efivars_lock);
return -ENOSPC;
}
status = ops->set_variable(name, &vendor, attributes, size, data);
- spin_unlock_irqrestore(&__efivars->lock, flags);
+ up(&efivars_lock);
return efi_status_to_err(status);
}
@@ -771,8 +793,6 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
int strsize1, strsize2;
bool found = false;
- lockdep_assert_held(&__efivars->lock);
-
list_for_each_entry_safe(entry, n, head, list) {
strsize1 = ucs2_strsize(name, 1024);
strsize2 = ucs2_strsize(entry->var.VariableName, 1024);
@@ -814,10 +834,11 @@ int efivar_entry_size(struct efivar_entry *entry, unsigned long *size)
*size = 0;
- spin_lock_irq(&__efivars->lock);
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
status = ops->get_variable(entry->var.VariableName,
&entry->var.VendorGuid, NULL, size, NULL);
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
if (status != EFI_BUFFER_TOO_SMALL)
return efi_status_to_err(status);
@@ -843,8 +864,6 @@ int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
const struct efivar_operations *ops = __efivars->ops;
efi_status_t status;
- lockdep_assert_held(&__efivars->lock);
-
status = ops->get_variable(entry->var.VariableName,
&entry->var.VendorGuid,
attributes, size, data);
@@ -866,11 +885,12 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
const struct efivar_operations *ops = __efivars->ops;
efi_status_t status;
- spin_lock_irq(&__efivars->lock);
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
status = ops->get_variable(entry->var.VariableName,
&entry->var.VendorGuid,
attributes, size, data);
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
return efi_status_to_err(status);
}
@@ -917,7 +937,8 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
* set_variable call, and removal of the variable from the efivars
* list (in the case of an authenticated delete).
*/
- spin_lock_irq(&__efivars->lock);
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
/*
* Ensure that the available space hasn't shrunk below the safe level
@@ -957,7 +978,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
if (status == EFI_NOT_FOUND)
efivar_entry_list_del_unlock(entry);
else
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
if (status && status != EFI_BUFFER_TOO_SMALL)
return efi_status_to_err(status);
@@ -965,7 +986,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
return 0;
out:
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
return err;
}
@@ -978,9 +999,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_set_get_size);
* efivar_entry_iter_end() is called. This function is usually used in
* conjunction with __efivar_entry_iter() or efivar_entry_iter().
*/
-void efivar_entry_iter_begin(void)
+int efivar_entry_iter_begin(void)
{
- spin_lock_irq(&__efivars->lock);
+ return down_interruptible(&efivars_lock);
}
EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
@@ -991,7 +1012,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
*/
void efivar_entry_iter_end(void)
{
- spin_unlock_irq(&__efivars->lock);
+ up(&efivars_lock);
}
EXPORT_SYMBOL_GPL(efivar_entry_iter_end);
@@ -1067,7 +1088,9 @@ int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
{
int err = 0;
- efivar_entry_iter_begin();
+ err = efivar_entry_iter_begin();
+ if (err)
+ return err;
err = __efivar_entry_iter(func, head, data, NULL);
efivar_entry_iter_end();
@@ -1112,12 +1135,18 @@ int efivars_register(struct efivars *efivars,
const struct efivar_operations *ops,
struct kobject *kobject)
{
- spin_lock_init(&efivars->lock);
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
+
efivars->ops = ops;
efivars->kobject = kobject;
__efivars = efivars;
+ pr_info("Registered efivars operations\n");
+
+ up(&efivars_lock);
+
return 0;
}
EXPORT_SYMBOL_GPL(efivars_register);
@@ -1133,6 +1162,9 @@ int efivars_unregister(struct efivars *efivars)
{
int rv;
+ if (down_interruptible(&efivars_lock))
+ return -EINTR;
+
if (!__efivars) {
printk(KERN_ERR "efivars not registered\n");
rv = -EINVAL;
@@ -1144,10 +1176,12 @@ int efivars_unregister(struct efivars *efivars)
goto out;
}
+ pr_info("Unregistered efivars operations\n");
__efivars = NULL;
rv = 0;
out:
+ up(&efivars_lock);
return rv;
}
EXPORT_SYMBOL_GPL(efivars_unregister);
diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c
index f1ab05e..c463871 100644
--- a/drivers/firmware/google/gsmi.c
+++ b/drivers/firmware/google/gsmi.c
@@ -910,8 +910,7 @@ out_err:
gsmi_buf_free(gsmi_dev.param_buf);
gsmi_buf_free(gsmi_dev.data_buf);
gsmi_buf_free(gsmi_dev.name_buf);
- if (gsmi_dev.dma_pool)
- dma_pool_destroy(gsmi_dev.dma_pool);
+ dma_pool_destroy(gsmi_dev.dma_pool);
platform_device_unregister(gsmi_dev.pdev);
pr_info("gsmi: failed to load: %d\n", ret);
return ret;
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index d614102..cd84934 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -21,6 +21,7 @@ config FPGA_MGR_SOCFPGA
config FPGA_MGR_ZYNQ_FPGA
tristate "Xilinx Zynq FPGA"
+ depends on ARCH_ZYNQ || COMPILE_TEST
depends on HAS_DMA
help
FPGA manager driver support for Xilinx Zynq FPGAs.
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index df7ab245..39c01b9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1708,11 +1708,11 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
DRM_INFO("amdgpu: finishing device.\n");
adev->shutdown = true;
+ drm_crtc_force_disable_all(adev->ddev);
/* evict vram memory */
amdgpu_bo_evict_vram(adev);
amdgpu_ib_pool_fini(adev);
amdgpu_fence_driver_fini(adev);
- drm_crtc_force_disable_all(adev->ddev);
amdgpu_fbdev_fini(adev);
r = amdgpu_fini(adev);
kfree(adev->ip_block_status);
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/device.h b/drivers/gpu/drm/nouveau/include/nvkm/core/device.h
index 7ea8aa7..6bc712f 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/core/device.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/core/device.h
@@ -175,6 +175,7 @@ struct nvkm_device_func {
void (*fini)(struct nvkm_device *, bool suspend);
resource_size_t (*resource_addr)(struct nvkm_device *, unsigned bar);
resource_size_t (*resource_size)(struct nvkm_device *, unsigned bar);
+ bool cpu_coherent;
};
struct nvkm_device_quirk {
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index 6190035..864323b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -209,7 +209,8 @@ nouveau_bo_new(struct drm_device *dev, int size, int align,
nvbo->tile_flags = tile_flags;
nvbo->bo.bdev = &drm->ttm.bdev;
- nvbo->force_coherent = flags & TTM_PL_FLAG_UNCACHED;
+ if (!nvxx_device(&drm->device)->func->cpu_coherent)
+ nvbo->force_coherent = flags & TTM_PL_FLAG_UNCACHED;
nvbo->page_shift = 12;
if (drm->client.vm) {
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c
index b1b6932..62ad030 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/pci.c
@@ -1614,6 +1614,7 @@ nvkm_device_pci_func = {
.fini = nvkm_device_pci_fini,
.resource_addr = nvkm_device_pci_resource_addr,
.resource_size = nvkm_device_pci_resource_size,
+ .cpu_coherent = !IS_ENABLED(CONFIG_ARM),
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
index 939682f..9b638bd 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
@@ -245,6 +245,7 @@ nvkm_device_tegra_func = {
.fini = nvkm_device_tegra_fini,
.resource_addr = nvkm_device_tegra_resource_addr,
.resource_size = nvkm_device_tegra_resource_size,
+ .cpu_coherent = false,
};
int
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
index edec30f..0a7b6ed 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/dmanv04.c
@@ -37,7 +37,10 @@ nv04_fifo_dma_object_dtor(struct nvkm_fifo_chan *base, int cookie)
{
struct nv04_fifo_chan *chan = nv04_fifo_chan(base);
struct nvkm_instmem *imem = chan->fifo->base.engine.subdev.device->imem;
+
+ mutex_lock(&chan->fifo->base.engine.subdev.mutex);
nvkm_ramht_remove(imem->ramht, cookie);
+ mutex_unlock(&chan->fifo->base.engine.subdev.mutex);
}
static int
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index e6abc09..1f78ec2 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -3015,6 +3015,12 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev,
if (rdev->pdev->device == 0x6811 &&
rdev->pdev->revision == 0x81)
max_mclk = 120000;
+ /* limit sclk/mclk on Jet parts for stability */
+ if (rdev->pdev->device == 0x6665 &&
+ rdev->pdev->revision == 0xc3) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
if (rps->vce_active) {
rps->evclk = rdev->pm.dpm.vce_states[rdev->pm.dpm.vce_level].evclk;
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 9688bfa..611b6b9 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -122,7 +122,7 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
return 0;
cmd = urb->transfer_buffer;
- for (i = y; i < height ; i++) {
+ for (i = y; i < y + height ; i++) {
const int line_offset = fb->base.pitches[0] * i;
const int byte_offset = line_offset + (x * bpp);
const int dev_byte_offset = (fb->base.width * bpp * i) + (x * bpp);
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 99ec3ff..7f8ff39 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -779,19 +779,8 @@ static struct miscdevice uhid_misc = {
.minor = UHID_MINOR,
.name = UHID_NAME,
};
+module_misc_device(uhid_misc);
-static int __init uhid_init(void)
-{
- return misc_register(&uhid_misc);
-}
-
-static void __exit uhid_exit(void)
-{
- misc_deregister(&uhid_misc);
-}
-
-module_init(uhid_init);
-module_exit(uhid_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index 56dd261..16f91c8 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -43,7 +43,12 @@ static void vmbus_setevent(struct vmbus_channel *channel)
{
struct hv_monitor_page *monitorpage;
- if (channel->offermsg.monitor_allocated) {
+ /*
+ * For channels marked as in "low latency" mode
+ * bypass the monitor page mechanism.
+ */
+ if ((channel->offermsg.monitor_allocated) &&
+ (!channel->low_latency)) {
/* Each u32 represents 32 channels */
sync_set_bit(channel->offermsg.child_relid & 31,
(unsigned long *) vmbus_connection.send_int_page +
@@ -70,12 +75,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
{
struct vmbus_channel_open_channel *open_msg;
struct vmbus_channel_msginfo *open_info = NULL;
- void *in, *out;
unsigned long flags;
int ret, err = 0;
- unsigned long t;
struct page *page;
+ if (send_ringbuffer_size % PAGE_SIZE ||
+ recv_ringbuffer_size % PAGE_SIZE)
+ return -EINVAL;
+
spin_lock_irqsave(&newchannel->lock, flags);
if (newchannel->state == CHANNEL_OPEN_STATE) {
newchannel->state = CHANNEL_OPENING_STATE;
@@ -95,36 +102,33 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
recv_ringbuffer_size));
if (!page)
- out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
- get_order(send_ringbuffer_size +
- recv_ringbuffer_size));
- else
- out = (void *)page_address(page);
+ page = alloc_pages(GFP_KERNEL|__GFP_ZERO,
+ get_order(send_ringbuffer_size +
+ recv_ringbuffer_size));
- if (!out) {
+ if (!page) {
err = -ENOMEM;
- goto error0;
+ goto error_set_chnstate;
}
- in = (void *)((unsigned long)out + send_ringbuffer_size);
-
- newchannel->ringbuffer_pages = out;
+ newchannel->ringbuffer_pages = page_address(page);
newchannel->ringbuffer_pagecount = (send_ringbuffer_size +
recv_ringbuffer_size) >> PAGE_SHIFT;
- ret = hv_ringbuffer_init(
- &newchannel->outbound, out, send_ringbuffer_size);
+ ret = hv_ringbuffer_init(&newchannel->outbound, page,
+ send_ringbuffer_size >> PAGE_SHIFT);
if (ret != 0) {
err = ret;
- goto error0;
+ goto error_free_pages;
}
- ret = hv_ringbuffer_init(
- &newchannel->inbound, in, recv_ringbuffer_size);
+ ret = hv_ringbuffer_init(&newchannel->inbound,
+ &page[send_ringbuffer_size >> PAGE_SHIFT],
+ recv_ringbuffer_size >> PAGE_SHIFT);
if (ret != 0) {
err = ret;
- goto error0;
+ goto error_free_pages;
}
@@ -132,14 +136,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
newchannel->ringbuffer_gpadlhandle = 0;
ret = vmbus_establish_gpadl(newchannel,
- newchannel->outbound.ring_buffer,
- send_ringbuffer_size +
- recv_ringbuffer_size,
- &newchannel->ringbuffer_gpadlhandle);
+ page_address(page),
+ send_ringbuffer_size +
+ recv_ringbuffer_size,
+ &newchannel->ringbuffer_gpadlhandle);
if (ret != 0) {
err = ret;
- goto error0;
+ goto error_free_pages;
}
/* Create and init the channel open message */
@@ -148,7 +152,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
GFP_KERNEL);
if (!open_info) {
err = -ENOMEM;
- goto error_gpadl;
+ goto error_free_gpadl;
}
init_completion(&open_info->waitevent);
@@ -164,7 +168,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
if (userdatalen > MAX_USER_DEFINED_BYTES) {
err = -EINVAL;
- goto error_gpadl;
+ goto error_free_gpadl;
}
if (userdatalen)
@@ -180,14 +184,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
if (ret != 0) {
err = ret;
- goto error1;
+ goto error_clean_msglist;
}
- t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ);
- if (t == 0) {
- err = -ETIMEDOUT;
- goto error1;
- }
+ wait_for_completion(&open_info->waitevent);
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&open_info->msglistentry);
@@ -195,25 +195,27 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
if (open_info->response.open_result.status) {
err = -EAGAIN;
- goto error_gpadl;
+ goto error_free_gpadl;
}
newchannel->state = CHANNEL_OPENED_STATE;
kfree(open_info);
return 0;
-error1:
+error_clean_msglist:
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&open_info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
-error_gpadl:
+error_free_gpadl:
vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle);
-
-error0:
- free_pages((unsigned long)out,
- get_order(send_ringbuffer_size + recv_ringbuffer_size));
kfree(open_info);
+error_free_pages:
+ hv_ringbuffer_cleanup(&newchannel->outbound);
+ hv_ringbuffer_cleanup(&newchannel->inbound);
+ __free_pages(page,
+ get_order(send_ringbuffer_size + recv_ringbuffer_size));
+error_set_chnstate:
newchannel->state = CHANNEL_OPEN_STATE;
return err;
}
@@ -238,8 +240,7 @@ EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
* create_gpadl_header - Creates a gpadl for the specified buffer
*/
static int create_gpadl_header(void *kbuffer, u32 size,
- struct vmbus_channel_msginfo **msginfo,
- u32 *messagecount)
+ struct vmbus_channel_msginfo **msginfo)
{
int i;
int pagecount;
@@ -283,7 +284,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
gpadl_header->range[0].pfn_array[i] = slow_virt_to_phys(
kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT;
*msginfo = msgheader;
- *messagecount = 1;
pfnsum = pfncount;
pfnleft = pagecount - pfncount;
@@ -323,7 +323,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
}
msgbody->msgsize = msgsize;
- (*messagecount)++;
gpadl_body =
(struct vmbus_channel_gpadl_body *)msgbody->msg;
@@ -352,6 +351,8 @@ static int create_gpadl_header(void *kbuffer, u32 size,
msgheader = kzalloc(msgsize, GFP_KERNEL);
if (msgheader == NULL)
goto nomem;
+
+ INIT_LIST_HEAD(&msgheader->submsglist);
msgheader->msgsize = msgsize;
gpadl_header = (struct vmbus_channel_gpadl_header *)
@@ -366,7 +367,6 @@ static int create_gpadl_header(void *kbuffer, u32 size,
kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT;
*msginfo = msgheader;
- *messagecount = 1;
}
return 0;
@@ -390,8 +390,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
struct vmbus_channel_gpadl_header *gpadlmsg;
struct vmbus_channel_gpadl_body *gpadl_body;
struct vmbus_channel_msginfo *msginfo = NULL;
- struct vmbus_channel_msginfo *submsginfo;
- u32 msgcount;
+ struct vmbus_channel_msginfo *submsginfo, *tmp;
struct list_head *curr;
u32 next_gpadl_handle;
unsigned long flags;
@@ -400,7 +399,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
next_gpadl_handle =
(atomic_inc_return(&vmbus_connection.next_gpadl_handle) - 1);
- ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount);
+ ret = create_gpadl_header(kbuffer, size, &msginfo);
if (ret)
return ret;
@@ -423,24 +422,21 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
if (ret != 0)
goto cleanup;
- if (msgcount > 1) {
- list_for_each(curr, &msginfo->submsglist) {
-
- submsginfo = (struct vmbus_channel_msginfo *)curr;
- gpadl_body =
- (struct vmbus_channel_gpadl_body *)submsginfo->msg;
+ list_for_each(curr, &msginfo->submsglist) {
+ submsginfo = (struct vmbus_channel_msginfo *)curr;
+ gpadl_body =
+ (struct vmbus_channel_gpadl_body *)submsginfo->msg;
- gpadl_body->header.msgtype =
- CHANNELMSG_GPADL_BODY;
- gpadl_body->gpadl = next_gpadl_handle;
+ gpadl_body->header.msgtype =
+ CHANNELMSG_GPADL_BODY;
+ gpadl_body->gpadl = next_gpadl_handle;
- ret = vmbus_post_msg(gpadl_body,
- submsginfo->msgsize -
- sizeof(*submsginfo));
- if (ret != 0)
- goto cleanup;
+ ret = vmbus_post_msg(gpadl_body,
+ submsginfo->msgsize -
+ sizeof(*submsginfo));
+ if (ret != 0)
+ goto cleanup;
- }
}
wait_for_completion(&msginfo->waitevent);
@@ -451,6 +447,10 @@ cleanup:
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
+ list_for_each_entry_safe(submsginfo, tmp, &msginfo->submsglist,
+ msglistentry) {
+ kfree(submsginfo);
+ }
kfree(msginfo);
return ret;
@@ -512,7 +512,6 @@ static void reset_channel_cb(void *arg)
static int vmbus_close_internal(struct vmbus_channel *channel)
{
struct vmbus_channel_close_channel *msg;
- struct tasklet_struct *tasklet;
int ret;
/*
@@ -524,8 +523,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
* To resolve the race, we can serialize them by disabling the
* tasklet when the latter is running here.
*/
- tasklet = hv_context.event_dpc[channel->target_cpu];
- tasklet_disable(tasklet);
+ hv_event_tasklet_disable(channel);
/*
* In case a device driver's probe() fails (e.g.,
@@ -591,7 +589,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
out:
- tasklet_enable(tasklet);
+ hv_event_tasklet_enable(channel);
return ret;
}
@@ -659,7 +657,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs,
- &signal, lock);
+ &signal, lock, channel->signal_policy);
/*
* Signalling the host is conditional on many factors:
@@ -680,11 +678,6 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
* mechanism which can hurt the performance otherwise.
*/
- if (channel->signal_policy)
- signal = true;
- else
- kick_q = true;
-
if (((ret == 0) && kick_q && signal) ||
(ret && !is_hvsock_channel(channel)))
vmbus_setevent(channel);
@@ -777,7 +770,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
- &signal, lock);
+ &signal, lock, channel->signal_policy);
/*
* Signalling the host is conditional on many factors:
@@ -795,11 +788,6 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
* enough condition that it should not matter.
*/
- if (channel->signal_policy)
- signal = true;
- else
- kick_q = true;
-
if (((ret == 0) && kick_q && signal) || (ret))
vmbus_setevent(channel);
@@ -861,7 +849,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
- &signal, lock);
+ &signal, lock, channel->signal_policy);
if (ret == 0 && signal)
vmbus_setevent(channel);
@@ -926,7 +914,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3,
- &signal, lock);
+ &signal, lock, channel->signal_policy);
if (ret == 0 && signal)
vmbus_setevent(channel);
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index b6c1211..96a85cd 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -21,6 +21,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
+#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mm.h>
@@ -138,10 +139,32 @@ static const struct vmbus_device vmbus_devs[] = {
},
};
-static u16 hv_get_dev_type(const uuid_le *guid)
+static const struct {
+ uuid_le guid;
+} vmbus_unsupported_devs[] = {
+ { HV_AVMA1_GUID },
+ { HV_AVMA2_GUID },
+ { HV_RDV_GUID },
+};
+
+static bool is_unsupported_vmbus_devs(const uuid_le *guid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vmbus_unsupported_devs); i++)
+ if (!uuid_le_cmp(*guid, vmbus_unsupported_devs[i].guid))
+ return true;
+ return false;
+}
+
+static u16 hv_get_dev_type(const struct vmbus_channel *channel)
{
+ const uuid_le *guid = &channel->offermsg.offer.if_type;
u16 i;
+ if (is_hvsock_channel(channel) || is_unsupported_vmbus_devs(guid))
+ return HV_UNKOWN;
+
for (i = HV_IDE; i < HV_UNKOWN; i++) {
if (!uuid_le_cmp(*guid, vmbus_devs[i].guid))
return i;
@@ -251,14 +274,12 @@ EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
*/
static struct vmbus_channel *alloc_channel(void)
{
- static atomic_t chan_num = ATOMIC_INIT(0);
struct vmbus_channel *channel;
channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
if (!channel)
return NULL;
- channel->id = atomic_inc_return(&chan_num);
channel->acquire_ring_lock = true;
spin_lock_init(&channel->inbound_lock);
spin_lock_init(&channel->lock);
@@ -303,16 +324,32 @@ static void vmbus_release_relid(u32 relid)
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
}
+void hv_event_tasklet_disable(struct vmbus_channel *channel)
+{
+ struct tasklet_struct *tasklet;
+ tasklet = hv_context.event_dpc[channel->target_cpu];
+ tasklet_disable(tasklet);
+}
+
+void hv_event_tasklet_enable(struct vmbus_channel *channel)
+{
+ struct tasklet_struct *tasklet;
+ tasklet = hv_context.event_dpc[channel->target_cpu];
+ tasklet_enable(tasklet);
+
+ /* In case there is any pending event */
+ tasklet_schedule(tasklet);
+}
+
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
{
unsigned long flags;
struct vmbus_channel *primary_channel;
- vmbus_release_relid(relid);
-
BUG_ON(!channel->rescind);
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
+ hv_event_tasklet_disable(channel);
if (channel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(channel->target_cpu,
@@ -321,6 +358,7 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
percpu_channel_deq(channel);
put_cpu();
}
+ hv_event_tasklet_enable(channel);
if (channel->primary_channel == NULL) {
list_del(&channel->listentry);
@@ -338,8 +376,11 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
* We need to free the bit for init_vp_index() to work in the case
* of sub-channel, when we reload drivers like hv_netvsc.
*/
- cpumask_clear_cpu(channel->target_cpu,
- &primary_channel->alloced_cpus_in_node);
+ if (channel->affinity_policy == HV_LOCALIZED)
+ cpumask_clear_cpu(channel->target_cpu,
+ &primary_channel->alloced_cpus_in_node);
+
+ vmbus_release_relid(relid);
free_channel(channel);
}
@@ -405,10 +446,13 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
goto err_free_chan;
}
- dev_type = hv_get_dev_type(&newchannel->offermsg.offer.if_type);
+ dev_type = hv_get_dev_type(newchannel);
+ if (dev_type == HV_NIC)
+ set_channel_signal_state(newchannel, HV_SIGNAL_POLICY_EXPLICIT);
init_vp_index(newchannel, dev_type);
+ hv_event_tasklet_disable(newchannel);
if (newchannel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(newchannel->target_cpu,
@@ -418,6 +462,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
percpu_channel_enq(newchannel);
put_cpu();
}
+ hv_event_tasklet_enable(newchannel);
/*
* This state is used to indicate a successful open
@@ -463,12 +508,11 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
return;
err_deq_chan:
- vmbus_release_relid(newchannel->offermsg.child_relid);
-
mutex_lock(&vmbus_connection.channel_mutex);
list_del(&newchannel->listentry);
mutex_unlock(&vmbus_connection.channel_mutex);
+ hv_event_tasklet_disable(newchannel);
if (newchannel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(newchannel->target_cpu,
@@ -477,6 +521,9 @@ err_deq_chan:
percpu_channel_deq(newchannel);
put_cpu();
}
+ hv_event_tasklet_enable(newchannel);
+
+ vmbus_release_relid(newchannel->offermsg.child_relid);
err_free_chan:
free_channel(newchannel);
@@ -522,17 +569,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
}
/*
- * We distribute primary channels evenly across all the available
- * NUMA nodes and within the assigned NUMA node we will assign the
- * first available CPU to the primary channel.
- * The sub-channels will be assigned to the CPUs available in the
- * NUMA node evenly.
+ * Based on the channel affinity policy, we will assign the NUMA
+ * nodes.
*/
- if (!primary) {
+
+ if ((channel->affinity_policy == HV_BALANCED) || (!primary)) {
while (true) {
next_node = next_numa_node_id++;
- if (next_node == nr_node_ids)
+ if (next_node == nr_node_ids) {
next_node = next_numa_node_id = 0;
+ continue;
+ }
if (cpumask_empty(cpumask_of_node(next_node)))
continue;
break;
@@ -556,15 +603,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
cur_cpu = -1;
- /*
- * Normally Hyper-V host doesn't create more subchannels than there
- * are VCPUs on the node but it is possible when not all present VCPUs
- * on the node are initialized by guest. Clear the alloced_cpus_in_node
- * to start over.
- */
- if (cpumask_equal(&primary->alloced_cpus_in_node,
- cpumask_of_node(primary->numa_node)))
- cpumask_clear(&primary->alloced_cpus_in_node);
+ if (primary->affinity_policy == HV_LOCALIZED) {
+ /*
+ * Normally Hyper-V host doesn't create more subchannels
+ * than there are VCPUs on the node but it is possible when not
+ * all present VCPUs on the node are initialized by guest.
+ * Clear the alloced_cpus_in_node to start over.
+ */
+ if (cpumask_equal(&primary->alloced_cpus_in_node,
+ cpumask_of_node(primary->numa_node)))
+ cpumask_clear(&primary->alloced_cpus_in_node);
+ }
while (true) {
cur_cpu = cpumask_next(cur_cpu, &available_mask);
@@ -575,17 +624,24 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
continue;
}
- /*
- * NOTE: in the case of sub-channel, we clear the sub-channel
- * related bit(s) in primary->alloced_cpus_in_node in
- * hv_process_channel_removal(), so when we reload drivers
- * like hv_netvsc in SMP guest, here we're able to re-allocate
- * bit from primary->alloced_cpus_in_node.
- */
- if (!cpumask_test_cpu(cur_cpu,
- &primary->alloced_cpus_in_node)) {
- cpumask_set_cpu(cur_cpu,
- &primary->alloced_cpus_in_node);
+ if (primary->affinity_policy == HV_LOCALIZED) {
+ /*
+ * NOTE: in the case of sub-channel, we clear the
+ * sub-channel related bit(s) in
+ * primary->alloced_cpus_in_node in
+ * hv_process_channel_removal(), so when we
+ * reload drivers like hv_netvsc in SMP guest, here
+ * we're able to re-allocate
+ * bit from primary->alloced_cpus_in_node.
+ */
+ if (!cpumask_test_cpu(cur_cpu,
+ &primary->alloced_cpus_in_node)) {
+ cpumask_set_cpu(cur_cpu,
+ &primary->alloced_cpus_in_node);
+ cpumask_set_cpu(cur_cpu, alloced_mask);
+ break;
+ }
+ } else {
cpumask_set_cpu(cur_cpu, alloced_mask);
break;
}
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index fcf8a02..78e6368 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -439,7 +439,7 @@ int vmbus_post_msg(void *buffer, size_t buflen)
union hv_connection_id conn_id;
int ret = 0;
int retries = 0;
- u32 msec = 1;
+ u32 usec = 1;
conn_id.asu32 = 0;
conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
@@ -472,9 +472,9 @@ int vmbus_post_msg(void *buffer, size_t buflen)
}
retries++;
- msleep(msec);
- if (msec < 2048)
- msec *= 2;
+ udelay(usec);
+ if (usec < 2048)
+ usec *= 2;
}
return ret;
}
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index a1c086b..60dbd6c 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -278,7 +278,7 @@ cleanup:
*
* This routine is called normally during driver unloading or exiting.
*/
-void hv_cleanup(void)
+void hv_cleanup(bool crash)
{
union hv_x64_msr_hypercall_contents hypercall_msr;
@@ -288,7 +288,8 @@ void hv_cleanup(void)
if (hv_context.hypercall_page) {
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
- vfree(hv_context.hypercall_page);
+ if (!crash)
+ vfree(hv_context.hypercall_page);
hv_context.hypercall_page = NULL;
}
@@ -308,7 +309,8 @@ void hv_cleanup(void)
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64);
- vfree(hv_context.tsc_page);
+ if (!crash)
+ vfree(hv_context.tsc_page);
hv_context.tsc_page = NULL;
}
#endif
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index df35fb7..fdf8da9 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -430,16 +430,27 @@ struct dm_info_msg {
* currently hot added. We hot add in multiples of 128M
* chunks; it is possible that we may not be able to bring
* online all the pages in the region. The range
- * covered_end_pfn defines the pages that can
+ * covered_start_pfn:covered_end_pfn defines the pages that can
* be brough online.
*/
struct hv_hotadd_state {
struct list_head list;
unsigned long start_pfn;
+ unsigned long covered_start_pfn;
unsigned long covered_end_pfn;
unsigned long ha_end_pfn;
unsigned long end_pfn;
+ /*
+ * A list of gaps.
+ */
+ struct list_head gap_list;
+};
+
+struct hv_hotadd_gap {
+ struct list_head list;
+ unsigned long start_pfn;
+ unsigned long end_pfn;
};
struct balloon_state {
@@ -536,7 +547,11 @@ struct hv_dynmem_device {
*/
struct task_struct *thread;
- struct mutex ha_region_mutex;
+ /*
+ * Protects ha_region_list, num_pages_onlined counter and individual
+ * regions from ha_region_list.
+ */
+ spinlock_t ha_lock;
/*
* A list of hot-add regions.
@@ -560,18 +575,14 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
void *v)
{
struct memory_notify *mem = (struct memory_notify *)v;
+ unsigned long flags;
switch (val) {
- case MEM_GOING_ONLINE:
- mutex_lock(&dm_device.ha_region_mutex);
- break;
-
case MEM_ONLINE:
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
dm_device.num_pages_onlined += mem->nr_pages;
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
case MEM_CANCEL_ONLINE:
- if (val == MEM_ONLINE ||
- mutex_is_locked(&dm_device.ha_region_mutex))
- mutex_unlock(&dm_device.ha_region_mutex);
if (dm_device.ha_waiting) {
dm_device.ha_waiting = false;
complete(&dm_device.ol_waitevent);
@@ -579,10 +590,11 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
break;
case MEM_OFFLINE:
- mutex_lock(&dm_device.ha_region_mutex);
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
dm_device.num_pages_onlined -= mem->nr_pages;
- mutex_unlock(&dm_device.ha_region_mutex);
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
break;
+ case MEM_GOING_ONLINE:
case MEM_GOING_OFFLINE:
case MEM_CANCEL_OFFLINE:
break;
@@ -595,18 +607,46 @@ static struct notifier_block hv_memory_nb = {
.priority = 0
};
+/* Check if the particular page is backed and can be onlined and online it. */
+static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg)
+{
+ unsigned long cur_start_pgp;
+ unsigned long cur_end_pgp;
+ struct hv_hotadd_gap *gap;
+
+ cur_start_pgp = (unsigned long)pfn_to_page(has->covered_start_pfn);
+ cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
+
+ /* The page is not backed. */
+ if (((unsigned long)pg < cur_start_pgp) ||
+ ((unsigned long)pg >= cur_end_pgp))
+ return;
+
+ /* Check for gaps. */
+ list_for_each_entry(gap, &has->gap_list, list) {
+ cur_start_pgp = (unsigned long)
+ pfn_to_page(gap->start_pfn);
+ cur_end_pgp = (unsigned long)
+ pfn_to_page(gap->end_pfn);
+ if (((unsigned long)pg >= cur_start_pgp) &&
+ ((unsigned long)pg < cur_end_pgp)) {
+ return;
+ }
+ }
+
+ /* This frame is currently backed; online the page. */
+ __online_page_set_limits(pg);
+ __online_page_increment_counters(pg);
+ __online_page_free(pg);
+}
-static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
+static void hv_bring_pgs_online(struct hv_hotadd_state *has,
+ unsigned long start_pfn, unsigned long size)
{
int i;
- for (i = 0; i < size; i++) {
- struct page *pg;
- pg = pfn_to_page(start_pfn + i);
- __online_page_set_limits(pg);
- __online_page_increment_counters(pg);
- __online_page_free(pg);
- }
+ for (i = 0; i < size; i++)
+ hv_page_online_one(has, pfn_to_page(start_pfn + i));
}
static void hv_mem_hot_add(unsigned long start, unsigned long size,
@@ -618,9 +658,12 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
unsigned long start_pfn;
unsigned long processed_pfn;
unsigned long total_pfn = pfn_count;
+ unsigned long flags;
for (i = 0; i < (size/HA_CHUNK); i++) {
start_pfn = start + (i * HA_CHUNK);
+
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
has->ha_end_pfn += HA_CHUNK;
if (total_pfn > HA_CHUNK) {
@@ -632,11 +675,11 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
}
has->covered_end_pfn += processed_pfn;
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
init_completion(&dm_device.ol_waitevent);
- dm_device.ha_waiting = true;
+ dm_device.ha_waiting = !memhp_auto_online;
- mutex_unlock(&dm_device.ha_region_mutex);
nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn));
ret = add_memory(nid, PFN_PHYS((start_pfn)),
(HA_CHUNK << PAGE_SHIFT));
@@ -653,20 +696,23 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
*/
do_hot_add = false;
}
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
has->ha_end_pfn -= HA_CHUNK;
has->covered_end_pfn -= processed_pfn;
- mutex_lock(&dm_device.ha_region_mutex);
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
break;
}
/*
- * Wait for the memory block to be onlined.
- * Since the hot add has succeeded, it is ok to
- * proceed even if the pages in the hot added region
- * have not been "onlined" within the allowed time.
+ * Wait for the memory block to be onlined when memory onlining
+ * is done outside of kernel (memhp_auto_online). Since the hot
+ * add has succeeded, it is ok to proceed even if the pages in
+ * the hot added region have not been "onlined" within the
+ * allowed time.
*/
- wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
- mutex_lock(&dm_device.ha_region_mutex);
+ if (dm_device.ha_waiting)
+ wait_for_completion_timeout(&dm_device.ol_waitevent,
+ 5*HZ);
post_status(&dm_device);
}
@@ -675,47 +721,64 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
static void hv_online_page(struct page *pg)
{
- struct list_head *cur;
struct hv_hotadd_state *has;
unsigned long cur_start_pgp;
unsigned long cur_end_pgp;
+ unsigned long flags;
- list_for_each(cur, &dm_device.ha_region_list) {
- has = list_entry(cur, struct hv_hotadd_state, list);
- cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn);
- cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
+ list_for_each_entry(has, &dm_device.ha_region_list, list) {
+ cur_start_pgp = (unsigned long)
+ pfn_to_page(has->start_pfn);
+ cur_end_pgp = (unsigned long)pfn_to_page(has->end_pfn);
- if (((unsigned long)pg >= cur_start_pgp) &&
- ((unsigned long)pg < cur_end_pgp)) {
- /*
- * This frame is currently backed; online the
- * page.
- */
- __online_page_set_limits(pg);
- __online_page_increment_counters(pg);
- __online_page_free(pg);
- }
+ /* The page belongs to a different HAS. */
+ if (((unsigned long)pg < cur_start_pgp) ||
+ ((unsigned long)pg >= cur_end_pgp))
+ continue;
+
+ hv_page_online_one(has, pg);
+ break;
}
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
}
-static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
+static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
{
- struct list_head *cur;
struct hv_hotadd_state *has;
+ struct hv_hotadd_gap *gap;
unsigned long residual, new_inc;
+ int ret = 0;
+ unsigned long flags;
- if (list_empty(&dm_device.ha_region_list))
- return false;
-
- list_for_each(cur, &dm_device.ha_region_list) {
- has = list_entry(cur, struct hv_hotadd_state, list);
-
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
+ list_for_each_entry(has, &dm_device.ha_region_list, list) {
/*
* If the pfn range we are dealing with is not in the current
* "hot add block", move on.
*/
if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue;
+
+ /*
+ * If the current start pfn is not where the covered_end
+ * is, create a gap and update covered_end_pfn.
+ */
+ if (has->covered_end_pfn != start_pfn) {
+ gap = kzalloc(sizeof(struct hv_hotadd_gap), GFP_ATOMIC);
+ if (!gap) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ INIT_LIST_HEAD(&gap->list);
+ gap->start_pfn = has->covered_end_pfn;
+ gap->end_pfn = start_pfn;
+ list_add_tail(&gap->list, &has->gap_list);
+
+ has->covered_end_pfn = start_pfn;
+ }
+
/*
* If the current hot add-request extends beyond
* our current limit; extend it.
@@ -732,19 +795,12 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
has->end_pfn += new_inc;
}
- /*
- * If the current start pfn is not where the covered_end
- * is, update it.
- */
-
- if (has->covered_end_pfn != start_pfn)
- has->covered_end_pfn = start_pfn;
-
- return true;
-
+ ret = 1;
+ break;
}
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
- return false;
+ return ret;
}
static unsigned long handle_pg_range(unsigned long pg_start,
@@ -753,17 +809,13 @@ static unsigned long handle_pg_range(unsigned long pg_start,
unsigned long start_pfn = pg_start;
unsigned long pfn_cnt = pg_count;
unsigned long size;
- struct list_head *cur;
struct hv_hotadd_state *has;
unsigned long pgs_ol = 0;
unsigned long old_covered_state;
+ unsigned long res = 0, flags;
- if (list_empty(&dm_device.ha_region_list))
- return 0;
-
- list_for_each(cur, &dm_device.ha_region_list) {
- has = list_entry(cur, struct hv_hotadd_state, list);
-
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
+ list_for_each_entry(has, &dm_device.ha_region_list, list) {
/*
* If the pfn range we are dealing with is not in the current
* "hot add block", move on.
@@ -783,6 +835,8 @@ static unsigned long handle_pg_range(unsigned long pg_start,
if (pgs_ol > pfn_cnt)
pgs_ol = pfn_cnt;
+ has->covered_end_pfn += pgs_ol;
+ pfn_cnt -= pgs_ol;
/*
* Check if the corresponding memory block is already
* online by checking its last previously backed page.
@@ -791,10 +845,8 @@ static unsigned long handle_pg_range(unsigned long pg_start,
*/
if (start_pfn > has->start_pfn &&
!PageReserved(pfn_to_page(start_pfn - 1)))
- hv_bring_pgs_online(start_pfn, pgs_ol);
+ hv_bring_pgs_online(has, start_pfn, pgs_ol);
- has->covered_end_pfn += pgs_ol;
- pfn_cnt -= pgs_ol;
}
if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) {
@@ -813,17 +865,20 @@ static unsigned long handle_pg_range(unsigned long pg_start,
} else {
pfn_cnt = size;
}
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
hv_mem_hot_add(has->ha_end_pfn, size, pfn_cnt, has);
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
}
/*
* If we managed to online any pages that were given to us,
* we declare success.
*/
- return has->covered_end_pfn - old_covered_state;
-
+ res = has->covered_end_pfn - old_covered_state;
+ break;
}
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
- return 0;
+ return res;
}
static unsigned long process_hot_add(unsigned long pg_start,
@@ -832,13 +887,20 @@ static unsigned long process_hot_add(unsigned long pg_start,
unsigned long rg_size)
{
struct hv_hotadd_state *ha_region = NULL;
+ int covered;
+ unsigned long flags;
if (pfn_cnt == 0)
return 0;
- if (!dm_device.host_specified_ha_region)
- if (pfn_covered(pg_start, pfn_cnt))
+ if (!dm_device.host_specified_ha_region) {
+ covered = pfn_covered(pg_start, pfn_cnt);
+ if (covered < 0)
+ return 0;
+
+ if (covered)
goto do_pg_range;
+ }
/*
* If the host has specified a hot-add range; deal with it first.
@@ -850,12 +912,17 @@ static unsigned long process_hot_add(unsigned long pg_start,
return 0;
INIT_LIST_HEAD(&ha_region->list);
+ INIT_LIST_HEAD(&ha_region->gap_list);
- list_add_tail(&ha_region->list, &dm_device.ha_region_list);
ha_region->start_pfn = rg_start;
ha_region->ha_end_pfn = rg_start;
+ ha_region->covered_start_pfn = pg_start;
ha_region->covered_end_pfn = pg_start;
ha_region->end_pfn = rg_start + rg_size;
+
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
+ list_add_tail(&ha_region->list, &dm_device.ha_region_list);
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
}
do_pg_range:
@@ -882,7 +949,6 @@ static void hot_add_req(struct work_struct *dummy)
resp.hdr.size = sizeof(struct dm_hot_add_response);
#ifdef CONFIG_MEMORY_HOTPLUG
- mutex_lock(&dm_device.ha_region_mutex);
pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt;
@@ -916,7 +982,6 @@ static void hot_add_req(struct work_struct *dummy)
rg_start, rg_sz);
dm->num_pages_added += resp.page_count;
- mutex_unlock(&dm_device.ha_region_mutex);
#endif
/*
* The result field of the response structure has the
@@ -1010,7 +1075,6 @@ static unsigned long compute_balloon_floor(void)
static void post_status(struct hv_dynmem_device *dm)
{
struct dm_status status;
- struct sysinfo val;
unsigned long now = jiffies;
unsigned long last_post = last_post_time;
@@ -1022,7 +1086,6 @@ static void post_status(struct hv_dynmem_device *dm)
if (!time_after(now, (last_post_time + HZ)))
return;
- si_meminfo(&val);
memset(&status, 0, sizeof(struct dm_status));
status.hdr.type = DM_STATUS_REPORT;
status.hdr.size = sizeof(struct dm_status);
@@ -1038,7 +1101,7 @@ static void post_status(struct hv_dynmem_device *dm)
* num_pages_onlined) as committed to the host, otherwise it can try
* asking us to balloon them out.
*/
- status.num_avail = val.freeram;
+ status.num_avail = si_mem_available();
status.num_committed = vm_memory_committed() +
dm->num_pages_ballooned +
(dm->num_pages_added > dm->num_pages_onlined ?
@@ -1144,7 +1207,7 @@ static void balloon_up(struct work_struct *dummy)
int ret;
bool done = false;
int i;
- struct sysinfo val;
+ long avail_pages;
unsigned long floor;
/* The host balloons pages in 2M granularity. */
@@ -1156,12 +1219,12 @@ static void balloon_up(struct work_struct *dummy)
*/
alloc_unit = 512;
- si_meminfo(&val);
+ avail_pages = si_mem_available();
floor = compute_balloon_floor();
/* Refuse to balloon below the floor, keep the 2M granularity. */
- if (val.freeram < num_pages || val.freeram - num_pages < floor) {
- num_pages = val.freeram > floor ? (val.freeram - floor) : 0;
+ if (avail_pages < num_pages || avail_pages - num_pages < floor) {
+ num_pages = avail_pages > floor ? (avail_pages - floor) : 0;
num_pages -= num_pages % PAGES_IN_2M;
}
@@ -1172,7 +1235,6 @@ static void balloon_up(struct work_struct *dummy)
bl_resp->hdr.size = sizeof(struct dm_balloon_response);
bl_resp->more_pages = 1;
-
num_pages -= num_ballooned;
num_ballooned = alloc_balloon_pages(&dm_device, num_pages,
bl_resp, alloc_unit);
@@ -1461,7 +1523,7 @@ static int balloon_probe(struct hv_device *dev,
init_completion(&dm_device.host_event);
init_completion(&dm_device.config_event);
INIT_LIST_HEAD(&dm_device.ha_region_list);
- mutex_init(&dm_device.ha_region_mutex);
+ spin_lock_init(&dm_device.ha_lock);
INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up);
INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req);
dm_device.host_specified_ha_region = false;
@@ -1580,8 +1642,9 @@ probe_error0:
static int balloon_remove(struct hv_device *dev)
{
struct hv_dynmem_device *dm = hv_get_drvdata(dev);
- struct list_head *cur, *tmp;
- struct hv_hotadd_state *has;
+ struct hv_hotadd_state *has, *tmp;
+ struct hv_hotadd_gap *gap, *tmp_gap;
+ unsigned long flags;
if (dm->num_pages_ballooned != 0)
pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned);
@@ -1596,11 +1659,16 @@ static int balloon_remove(struct hv_device *dev)
restore_online_page_callback(&hv_online_page);
unregister_memory_notifier(&hv_memory_nb);
#endif
- list_for_each_safe(cur, tmp, &dm->ha_region_list) {
- has = list_entry(cur, struct hv_hotadd_state, list);
+ spin_lock_irqsave(&dm_device.ha_lock, flags);
+ list_for_each_entry_safe(has, tmp, &dm->ha_region_list, list) {
+ list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) {
+ list_del(&gap->list);
+ kfree(gap);
+ }
list_del(&has->list);
kfree(has);
}
+ spin_unlock_irqrestore(&dm_device.ha_lock, flags);
return 0;
}
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index 23c7079..8b2ba98 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -83,6 +83,12 @@ static void fcopy_timeout_func(struct work_struct *dummy)
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
}
+static void fcopy_register_done(void)
+{
+ pr_debug("FCP: userspace daemon registered\n");
+ hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
+}
+
static int fcopy_handle_handshake(u32 version)
{
u32 our_ver = FCOPY_CURRENT_VERSION;
@@ -94,7 +100,8 @@ static int fcopy_handle_handshake(u32 version)
break;
case FCOPY_VERSION_1:
/* Daemon expects us to reply with our own version */
- if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver)))
+ if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
+ fcopy_register_done))
return -EFAULT;
dm_reg_value = version;
break;
@@ -107,8 +114,7 @@ static int fcopy_handle_handshake(u32 version)
*/
return -EINVAL;
}
- pr_debug("FCP: userspace daemon ver. %d registered\n", version);
- hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
+ pr_debug("FCP: userspace daemon ver. %d connected\n", version);
return 0;
}
@@ -161,7 +167,7 @@ static void fcopy_send_data(struct work_struct *dummy)
}
fcopy_transaction.state = HVUTIL_USERSPACE_REQ;
- rc = hvutil_transport_send(hvt, out_src, out_len);
+ rc = hvutil_transport_send(hvt, out_src, out_len, NULL);
if (rc) {
pr_debug("FCP: failed to communicate to the daemon: %d\n", rc);
if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index cb1a916..5e1fdc8 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -102,6 +102,17 @@ static void kvp_poll_wrapper(void *channel)
hv_kvp_onchannelcallback(channel);
}
+static void kvp_register_done(void)
+{
+ /*
+ * If we're still negotiating with the host cancel the timeout
+ * work to not poll the channel twice.
+ */
+ pr_debug("KVP: userspace daemon registered\n");
+ cancel_delayed_work_sync(&kvp_host_handshake_work);
+ hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
+}
+
static void
kvp_register(int reg_value)
{
@@ -116,7 +127,8 @@ kvp_register(int reg_value)
kvp_msg->kvp_hdr.operation = reg_value;
strcpy(version, HV_DRV_VERSION);
- hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg));
+ hvutil_transport_send(hvt, kvp_msg, sizeof(*kvp_msg),
+ kvp_register_done);
kfree(kvp_msg);
}
}
@@ -158,17 +170,10 @@ static int kvp_handle_handshake(struct hv_kvp_msg *msg)
/*
* We have a compatible daemon; complete the handshake.
*/
- pr_debug("KVP: userspace daemon ver. %d registered\n",
- KVP_OP_REGISTER);
+ pr_debug("KVP: userspace daemon ver. %d connected\n",
+ msg->kvp_hdr.operation);
kvp_register(dm_reg_value);
- /*
- * If we're still negotiating with the host cancel the timeout
- * work to not poll the channel twice.
- */
- cancel_delayed_work_sync(&kvp_host_handshake_work);
- hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
-
return 0;
}
@@ -455,7 +460,7 @@ kvp_send_key(struct work_struct *dummy)
}
kvp_transaction.state = HVUTIL_USERSPACE_REQ;
- rc = hvutil_transport_send(hvt, message, sizeof(*message));
+ rc = hvutil_transport_send(hvt, message, sizeof(*message), NULL);
if (rc) {
pr_debug("KVP: failed to communicate to the daemon: %d\n", rc);
if (cancel_delayed_work_sync(&kvp_timeout_work)) {
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
index 3fba14e..a670713 100644
--- a/drivers/hv/hv_snapshot.c
+++ b/drivers/hv/hv_snapshot.c
@@ -67,11 +67,11 @@ static const char vss_devname[] = "vmbus/hv_vss";
static __u8 *recv_buffer;
static struct hvutil_transport *hvt;
-static void vss_send_op(struct work_struct *dummy);
static void vss_timeout_func(struct work_struct *dummy);
+static void vss_handle_request(struct work_struct *dummy);
static DECLARE_DELAYED_WORK(vss_timeout_work, vss_timeout_func);
-static DECLARE_WORK(vss_send_op_work, vss_send_op);
+static DECLARE_WORK(vss_handle_request_work, vss_handle_request);
static void vss_poll_wrapper(void *channel)
{
@@ -95,6 +95,12 @@ static void vss_timeout_func(struct work_struct *dummy)
hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
}
+static void vss_register_done(void)
+{
+ hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
+ pr_debug("VSS: userspace daemon registered\n");
+}
+
static int vss_handle_handshake(struct hv_vss_msg *vss_msg)
{
u32 our_ver = VSS_OP_REGISTER1;
@@ -105,16 +111,16 @@ static int vss_handle_handshake(struct hv_vss_msg *vss_msg)
dm_reg_value = VSS_OP_REGISTER;
break;
case VSS_OP_REGISTER1:
- /* Daemon expects us to reply with our own version*/
- if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver)))
+ /* Daemon expects us to reply with our own version */
+ if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
+ vss_register_done))
return -EFAULT;
dm_reg_value = VSS_OP_REGISTER1;
break;
default:
return -EINVAL;
}
- hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
- pr_debug("VSS: userspace daemon ver. %d registered\n", dm_reg_value);
+ pr_debug("VSS: userspace daemon ver. %d connected\n", dm_reg_value);
return 0;
}
@@ -136,6 +142,11 @@ static int vss_on_msg(void *msg, int len)
return vss_handle_handshake(vss_msg);
} else if (vss_transaction.state == HVUTIL_USERSPACE_REQ) {
vss_transaction.state = HVUTIL_USERSPACE_RECV;
+
+ if (vss_msg->vss_hdr.operation == VSS_OP_HOT_BACKUP)
+ vss_transaction.msg->vss_cf.flags =
+ VSS_HBU_NO_AUTO_RECOVERY;
+
if (cancel_delayed_work_sync(&vss_timeout_work)) {
vss_respond_to_host(vss_msg->error);
/* Transaction is finished, reset the state. */
@@ -150,8 +161,7 @@ static int vss_on_msg(void *msg, int len)
return 0;
}
-
-static void vss_send_op(struct work_struct *dummy)
+static void vss_send_op(void)
{
int op = vss_transaction.msg->vss_hdr.operation;
int rc;
@@ -168,7 +178,10 @@ static void vss_send_op(struct work_struct *dummy)
vss_msg->vss_hdr.operation = op;
vss_transaction.state = HVUTIL_USERSPACE_REQ;
- rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg));
+
+ schedule_delayed_work(&vss_timeout_work, VSS_USERSPACE_TIMEOUT);
+
+ rc = hvutil_transport_send(hvt, vss_msg, sizeof(*vss_msg), NULL);
if (rc) {
pr_warn("VSS: failed to communicate to the daemon: %d\n", rc);
if (cancel_delayed_work_sync(&vss_timeout_work)) {
@@ -182,6 +195,38 @@ static void vss_send_op(struct work_struct *dummy)
return;
}
+static void vss_handle_request(struct work_struct *dummy)
+{
+ switch (vss_transaction.msg->vss_hdr.operation) {
+ /*
+ * Initiate a "freeze/thaw" operation in the guest.
+ * We respond to the host once the operation is complete.
+ *
+ * We send the message to the user space daemon and the operation is
+ * performed in the daemon.
+ */
+ case VSS_OP_THAW:
+ case VSS_OP_FREEZE:
+ case VSS_OP_HOT_BACKUP:
+ if (vss_transaction.state < HVUTIL_READY) {
+ /* Userspace is not registered yet */
+ vss_respond_to_host(HV_E_FAIL);
+ return;
+ }
+ vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
+ vss_send_op();
+ return;
+ case VSS_OP_GET_DM_INFO:
+ vss_transaction.msg->dm_info.flags = 0;
+ break;
+ default:
+ break;
+ }
+
+ vss_respond_to_host(0);
+ hv_poll_channel(vss_transaction.recv_channel, vss_poll_wrapper);
+}
+
/*
* Send a response back to the host.
*/
@@ -266,48 +311,8 @@ void hv_vss_onchannelcallback(void *context)
vss_transaction.recv_req_id = requestid;
vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
- switch (vss_msg->vss_hdr.operation) {
- /*
- * Initiate a "freeze/thaw"
- * operation in the guest.
- * We respond to the host once
- * the operation is complete.
- *
- * We send the message to the
- * user space daemon and the
- * operation is performed in
- * the daemon.
- */
- case VSS_OP_FREEZE:
- case VSS_OP_THAW:
- if (vss_transaction.state < HVUTIL_READY) {
- /* Userspace is not registered yet */
- vss_respond_to_host(HV_E_FAIL);
- return;
- }
- vss_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
- schedule_work(&vss_send_op_work);
- schedule_delayed_work(&vss_timeout_work,
- VSS_USERSPACE_TIMEOUT);
- return;
-
- case VSS_OP_HOT_BACKUP:
- vss_msg->vss_cf.flags =
- VSS_HBU_NO_AUTO_RECOVERY;
- vss_respond_to_host(0);
- return;
-
- case VSS_OP_GET_DM_INFO:
- vss_msg->dm_info.flags = 0;
- vss_respond_to_host(0);
- return;
-
- default:
- vss_respond_to_host(0);
- return;
-
- }
-
+ schedule_work(&vss_handle_request_work);
+ return;
}
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
@@ -358,6 +363,6 @@ void hv_vss_deinit(void)
{
vss_transaction.state = HVUTIL_DEVICE_DYING;
cancel_delayed_work_sync(&vss_timeout_work);
- cancel_work_sync(&vss_send_op_work);
+ cancel_work_sync(&vss_handle_request_work);
hvutil_transport_destroy(hvt);
}
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index d5acaa2..4aa3cb6 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -34,22 +34,25 @@
#define SD_MINOR 0
#define SD_VERSION (SD_MAJOR << 16 | SD_MINOR)
-#define SD_WS2008_MAJOR 1
-#define SD_WS2008_VERSION (SD_WS2008_MAJOR << 16 | SD_MINOR)
+#define SD_MAJOR_1 1
+#define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR)
-#define TS_MAJOR 3
+#define TS_MAJOR 4
#define TS_MINOR 0
#define TS_VERSION (TS_MAJOR << 16 | TS_MINOR)
-#define TS_WS2008_MAJOR 1
-#define TS_WS2008_VERSION (TS_WS2008_MAJOR << 16 | TS_MINOR)
+#define TS_MAJOR_1 1
+#define TS_VERSION_1 (TS_MAJOR_1 << 16 | TS_MINOR)
+
+#define TS_MAJOR_3 3
+#define TS_VERSION_3 (TS_MAJOR_3 << 16 | TS_MINOR)
#define HB_MAJOR 3
-#define HB_MINOR 0
+#define HB_MINOR 0
#define HB_VERSION (HB_MAJOR << 16 | HB_MINOR)
-#define HB_WS2008_MAJOR 1
-#define HB_WS2008_VERSION (HB_WS2008_MAJOR << 16 | HB_MINOR)
+#define HB_MAJOR_1 1
+#define HB_VERSION_1 (HB_MAJOR_1 << 16 | HB_MINOR)
static int sd_srv_version;
static int ts_srv_version;
@@ -61,9 +64,14 @@ static struct hv_util_service util_shutdown = {
.util_cb = shutdown_onchannelcallback,
};
+static int hv_timesync_init(struct hv_util_service *srv);
+static void hv_timesync_deinit(void);
+
static void timesync_onchannelcallback(void *context);
static struct hv_util_service util_timesynch = {
.util_cb = timesync_onchannelcallback,
+ .util_init = hv_timesync_init,
+ .util_deinit = hv_timesync_deinit,
};
static void heartbeat_onchannelcallback(void *context);
@@ -161,35 +169,43 @@ static void shutdown_onchannelcallback(void *context)
}
/*
- * Set guest time to host UTC time.
- */
-static inline void do_adj_guesttime(u64 hosttime)
-{
- s64 host_tns;
- struct timespec host_ts;
-
- host_tns = (hosttime - WLTIMEDELTA) * 100;
- host_ts = ns_to_timespec(host_tns);
-
- do_settimeofday(&host_ts);
-}
-
-/*
* Set the host time in a process context.
*/
struct adj_time_work {
struct work_struct work;
u64 host_time;
+ u64 ref_time;
+ u8 flags;
};
static void hv_set_host_time(struct work_struct *work)
{
struct adj_time_work *wrk;
+ s64 host_tns;
+ u64 newtime;
+ struct timespec host_ts;
wrk = container_of(work, struct adj_time_work, work);
- do_adj_guesttime(wrk->host_time);
- kfree(wrk);
+
+ newtime = wrk->host_time;
+ if (ts_srv_version > TS_VERSION_3) {
+ /*
+ * Some latency has been introduced since Hyper-V generated
+ * its time sample. Take that latency into account before
+ * using TSC reference time sample from Hyper-V.
+ *
+ * This sample is given by TimeSync v4 and above hosts.
+ */
+ u64 current_tick;
+
+ rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
+ newtime += (current_tick - wrk->ref_time);
+ }
+ host_tns = (newtime - WLTIMEDELTA) * 100;
+ host_ts = ns_to_timespec(host_tns);
+
+ do_settimeofday(&host_ts);
}
/*
@@ -198,33 +214,31 @@ static void hv_set_host_time(struct work_struct *work)
* ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
* After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
* message after the timesync channel is opened. Since the hv_utils module is
- * loaded after hv_vmbus, the first message is usually missed. The other
- * thing is, systime is automatically set to emulated hardware clock which may
- * not be UTC time or in the same time zone. So, to override these effects, we
- * use the first 50 time samples for initial system time setting.
+ * loaded after hv_vmbus, the first message is usually missed. This bit is
+ * considered a hard request to discipline the clock.
+ *
+ * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is
+ * typically used as a hint to the guest. The guest is under no obligation
+ * to discipline the clock.
*/
-static inline void adj_guesttime(u64 hosttime, u8 flags)
+static struct adj_time_work wrk;
+static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 flags)
{
- struct adj_time_work *wrk;
- static s32 scnt = 50;
- wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC);
- if (wrk == NULL)
+ /*
+ * This check is safe since we are executing in the
+ * interrupt context and time synch messages arre always
+ * delivered on the same CPU.
+ */
+ if (work_pending(&wrk.work))
return;
- wrk->host_time = hosttime;
- if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
- INIT_WORK(&wrk->work, hv_set_host_time);
- schedule_work(&wrk->work);
- return;
+ wrk.host_time = hosttime;
+ wrk.ref_time = reftime;
+ wrk.flags = flags;
+ if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) {
+ schedule_work(&wrk.work);
}
-
- if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
- scnt--;
- INIT_WORK(&wrk->work, hv_set_host_time);
- schedule_work(&wrk->work);
- } else
- kfree(wrk);
}
/*
@@ -237,6 +251,7 @@ static void timesync_onchannelcallback(void *context)
u64 requestid;
struct icmsg_hdr *icmsghdrp;
struct ictimesync_data *timedatap;
+ struct ictimesync_ref_data *refdata;
u8 *time_txf_buf = util_timesynch.recv_buffer;
struct icmsg_negotiate *negop = NULL;
@@ -252,11 +267,27 @@ static void timesync_onchannelcallback(void *context)
time_txf_buf,
util_fw_version,
ts_srv_version);
+ pr_info("Using TimeSync version %d.%d\n",
+ ts_srv_version >> 16, ts_srv_version & 0xFFFF);
} else {
- timedatap = (struct ictimesync_data *)&time_txf_buf[
- sizeof(struct vmbuspipe_hdr) +
- sizeof(struct icmsg_hdr)];
- adj_guesttime(timedatap->parenttime, timedatap->flags);
+ if (ts_srv_version > TS_VERSION_3) {
+ refdata = (struct ictimesync_ref_data *)
+ &time_txf_buf[
+ sizeof(struct vmbuspipe_hdr) +
+ sizeof(struct icmsg_hdr)];
+
+ adj_guesttime(refdata->parenttime,
+ refdata->vmreferencetime,
+ refdata->flags);
+ } else {
+ timedatap = (struct ictimesync_data *)
+ &time_txf_buf[
+ sizeof(struct vmbuspipe_hdr) +
+ sizeof(struct icmsg_hdr)];
+ adj_guesttime(timedatap->parenttime,
+ 0,
+ timedatap->flags);
+ }
}
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
@@ -350,16 +381,21 @@ static int util_probe(struct hv_device *dev,
switch (vmbus_proto_version) {
case (VERSION_WS2008):
util_fw_version = UTIL_WS2K8_FW_VERSION;
- sd_srv_version = SD_WS2008_VERSION;
- ts_srv_version = TS_WS2008_VERSION;
- hb_srv_version = HB_WS2008_VERSION;
+ sd_srv_version = SD_VERSION_1;
+ ts_srv_version = TS_VERSION_1;
+ hb_srv_version = HB_VERSION_1;
break;
-
- default:
+ case(VERSION_WIN10):
util_fw_version = UTIL_FW_VERSION;
sd_srv_version = SD_VERSION;
ts_srv_version = TS_VERSION;
hb_srv_version = HB_VERSION;
+ break;
+ default:
+ util_fw_version = UTIL_FW_VERSION;
+ sd_srv_version = SD_VERSION;
+ ts_srv_version = TS_VERSION_3;
+ hb_srv_version = HB_VERSION;
}
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
@@ -427,6 +463,17 @@ static struct hv_driver util_drv = {
.remove = util_remove,
};
+static int hv_timesync_init(struct hv_util_service *srv)
+{
+ INIT_WORK(&wrk.work, hv_set_host_time);
+ return 0;
+}
+
+static void hv_timesync_deinit(void)
+{
+ cancel_work_sync(&wrk.work);
+}
+
static int __init init_hyperv_utils(void)
{
pr_info("Registering HyperV Utility Driver\n");
diff --git a/drivers/hv/hv_utils_transport.c b/drivers/hv/hv_utils_transport.c
index 9a9983f..c235a95 100644
--- a/drivers/hv/hv_utils_transport.c
+++ b/drivers/hv/hv_utils_transport.c
@@ -72,6 +72,10 @@ static ssize_t hvt_op_read(struct file *file, char __user *buf,
hvt->outmsg = NULL;
hvt->outmsg_len = 0;
+ if (hvt->on_read)
+ hvt->on_read();
+ hvt->on_read = NULL;
+
out_unlock:
mutex_unlock(&hvt->lock);
return ret;
@@ -219,7 +223,8 @@ static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
mutex_unlock(&hvt->lock);
}
-int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
+int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len,
+ void (*on_read_cb)(void))
{
struct cn_msg *cn_msg;
int ret = 0;
@@ -237,6 +242,13 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
memcpy(cn_msg->data, msg, len);
ret = cn_netlink_send(cn_msg, 0, 0, GFP_ATOMIC);
kfree(cn_msg);
+ /*
+ * We don't know when netlink messages are delivered but unlike
+ * in CHARDEV mode we're not blocked and we can send next
+ * messages right away.
+ */
+ if (on_read_cb)
+ on_read_cb();
return ret;
}
/* HVUTIL_TRANSPORT_CHARDEV */
@@ -255,6 +267,7 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len)
if (hvt->outmsg) {
memcpy(hvt->outmsg, msg, len);
hvt->outmsg_len = len;
+ hvt->on_read = on_read_cb;
wake_up_interruptible(&hvt->outmsg_q);
} else
ret = -ENOMEM;
diff --git a/drivers/hv/hv_utils_transport.h b/drivers/hv/hv_utils_transport.h
index 06254a1..d98f522 100644
--- a/drivers/hv/hv_utils_transport.h
+++ b/drivers/hv/hv_utils_transport.h
@@ -36,6 +36,7 @@ struct hvutil_transport {
struct list_head list; /* hvt_list */
int (*on_msg)(void *, int); /* callback on new user message */
void (*on_reset)(void); /* callback when userspace drops */
+ void (*on_read)(void); /* callback on message read */
u8 *outmsg; /* message to the userspace */
int outmsg_len; /* its length */
wait_queue_head_t outmsg_q; /* poll/read wait queue */
@@ -46,7 +47,8 @@ struct hvutil_transport *hvutil_transport_init(const char *name,
u32 cn_idx, u32 cn_val,
int (*on_msg)(void *, int),
void (*on_reset)(void));
-int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len);
+int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len,
+ void (*on_read_cb)(void));
void hvutil_transport_destroy(struct hvutil_transport *hvt);
#endif /* _HV_UTILS_TRANSPORT_H */
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 718b5c7..a5b4442 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -495,7 +495,7 @@ struct hv_ring_buffer_debug_info {
extern int hv_init(void);
-extern void hv_cleanup(void);
+extern void hv_cleanup(bool crash);
extern int hv_post_message(union hv_connection_id connection_id,
enum hv_message_type message_type,
@@ -522,14 +522,15 @@ extern unsigned int host_info_edx;
/* Interface */
-int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void *buffer,
- u32 buflen);
+int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
+ struct page *pages, u32 pagecnt);
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
int hv_ringbuffer_write(struct hv_ring_buffer_info *ring_info,
struct kvec *kv_list,
- u32 kv_count, bool *signal, bool lock);
+ u32 kv_count, bool *signal, bool lock,
+ enum hv_signal_policy policy);
int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info,
void *buffer, u32 buflen, u32 *buffer_actual_len,
diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
index fe586bf..08043da 100644
--- a/drivers/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -27,6 +27,8 @@
#include <linux/mm.h>
#include <linux/hyperv.h>
#include <linux/uio.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
#include "hyperv_vmbus.h"
@@ -66,12 +68,20 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
* arrived.
*/
-static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)
+static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi,
+ enum hv_signal_policy policy)
{
virt_mb();
if (READ_ONCE(rbi->ring_buffer->interrupt_mask))
return false;
+ /*
+ * When the client wants to control signaling,
+ * we only honour the host interrupt mask.
+ */
+ if (policy == HV_SIGNAL_POLICY_EXPLICIT)
+ return true;
+
/* check interrupt_mask before read_index */
virt_rmb();
/*
@@ -162,18 +172,7 @@ static u32 hv_copyfrom_ringbuffer(
void *ring_buffer = hv_get_ring_buffer(ring_info);
u32 ring_buffer_size = hv_get_ring_buffersize(ring_info);
- u32 frag_len;
-
- /* wrap-around detected at the src */
- if (destlen > ring_buffer_size - start_read_offset) {
- frag_len = ring_buffer_size - start_read_offset;
-
- memcpy(dest, ring_buffer + start_read_offset, frag_len);
- memcpy(dest + frag_len, ring_buffer, destlen - frag_len);
- } else
-
- memcpy(dest, ring_buffer + start_read_offset, destlen);
-
+ memcpy(dest, ring_buffer + start_read_offset, destlen);
start_read_offset += destlen;
start_read_offset %= ring_buffer_size;
@@ -194,15 +193,8 @@ static u32 hv_copyto_ringbuffer(
{
void *ring_buffer = hv_get_ring_buffer(ring_info);
u32 ring_buffer_size = hv_get_ring_buffersize(ring_info);
- u32 frag_len;
- /* wrap-around detected! */
- if (srclen > ring_buffer_size - start_write_offset) {
- frag_len = ring_buffer_size - start_write_offset;
- memcpy(ring_buffer + start_write_offset, src, frag_len);
- memcpy(ring_buffer, src + frag_len, srclen - frag_len);
- } else
- memcpy(ring_buffer + start_write_offset, src, srclen);
+ memcpy(ring_buffer + start_write_offset, src, srclen);
start_write_offset += srclen;
start_write_offset %= ring_buffer_size;
@@ -235,22 +227,46 @@ void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
/* Initialize the ring buffer. */
int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
- void *buffer, u32 buflen)
+ struct page *pages, u32 page_cnt)
{
- if (sizeof(struct hv_ring_buffer) != PAGE_SIZE)
- return -EINVAL;
+ int i;
+ struct page **pages_wraparound;
+
+ BUILD_BUG_ON((sizeof(struct hv_ring_buffer) != PAGE_SIZE));
memset(ring_info, 0, sizeof(struct hv_ring_buffer_info));
- ring_info->ring_buffer = (struct hv_ring_buffer *)buffer;
+ /*
+ * First page holds struct hv_ring_buffer, do wraparound mapping for
+ * the rest.
+ */
+ pages_wraparound = kzalloc(sizeof(struct page *) * (page_cnt * 2 - 1),
+ GFP_KERNEL);
+ if (!pages_wraparound)
+ return -ENOMEM;
+
+ pages_wraparound[0] = pages;
+ for (i = 0; i < 2 * (page_cnt - 1); i++)
+ pages_wraparound[i + 1] = &pages[i % (page_cnt - 1) + 1];
+
+ ring_info->ring_buffer = (struct hv_ring_buffer *)
+ vmap(pages_wraparound, page_cnt * 2 - 1, VM_MAP, PAGE_KERNEL);
+
+ kfree(pages_wraparound);
+
+
+ if (!ring_info->ring_buffer)
+ return -ENOMEM;
+
ring_info->ring_buffer->read_index =
ring_info->ring_buffer->write_index = 0;
/* Set the feature bit for enabling flow control. */
ring_info->ring_buffer->feature_bits.value = 1;
- ring_info->ring_size = buflen;
- ring_info->ring_datasize = buflen - sizeof(struct hv_ring_buffer);
+ ring_info->ring_size = page_cnt << PAGE_SHIFT;
+ ring_info->ring_datasize = ring_info->ring_size -
+ sizeof(struct hv_ring_buffer);
spin_lock_init(&ring_info->ring_lock);
@@ -260,11 +276,13 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
/* Cleanup the ring buffer. */
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
{
+ vunmap(ring_info->ring_buffer);
}
/* Write to the ring buffer. */
int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
- struct kvec *kv_list, u32 kv_count, bool *signal, bool lock)
+ struct kvec *kv_list, u32 kv_count, bool *signal, bool lock,
+ enum hv_signal_policy policy)
{
int i = 0;
u32 bytes_avail_towrite;
@@ -326,7 +344,7 @@ int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
if (lock)
spin_unlock_irqrestore(&outring_info->ring_lock, flags);
- *signal = hv_need_to_signal(old_write, outring_info);
+ *signal = hv_need_to_signal(old_write, outring_info, policy);
return 0;
}
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index e82f7e1..a259e18 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -105,8 +105,8 @@ static struct notifier_block hyperv_panic_block = {
static const char *fb_mmio_name = "fb_range";
static struct resource *fb_mmio;
-struct resource *hyperv_mmio;
-DEFINE_SEMAPHORE(hyperv_mmio_lock);
+static struct resource *hyperv_mmio;
+static DEFINE_SEMAPHORE(hyperv_mmio_lock);
static int vmbus_exists(void)
{
@@ -874,7 +874,7 @@ err_alloc:
bus_unregister(&hv_bus);
err_cleanup:
- hv_cleanup();
+ hv_cleanup(false);
return ret;
}
@@ -961,8 +961,8 @@ int vmbus_device_register(struct hv_device *child_device_obj)
{
int ret = 0;
- dev_set_name(&child_device_obj->device, "vmbus_%d",
- child_device_obj->channel->id);
+ dev_set_name(&child_device_obj->device, "vmbus-%pUl",
+ child_device_obj->channel->offermsg.offer.if_instance.b);
child_device_obj->device.bus = &hv_bus;
child_device_obj->device.parent = &hv_acpi_dev->dev;
@@ -1326,7 +1326,7 @@ static void hv_kexec_handler(void)
vmbus_initiate_unload(false);
for_each_online_cpu(cpu)
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
- hv_cleanup();
+ hv_cleanup(false);
};
static void hv_crash_handler(struct pt_regs *regs)
@@ -1338,7 +1338,7 @@ static void hv_crash_handler(struct pt_regs *regs)
* for kdump.
*/
hv_synic_cleanup(NULL);
- hv_cleanup();
+ hv_cleanup(true);
};
static int __init hv_acpi_init(void)
@@ -1398,7 +1398,7 @@ static void __exit vmbus_exit(void)
&hyperv_panic_block);
}
bus_unregister(&hv_bus);
- hv_cleanup();
+ hv_cleanup(false);
for_each_online_cpu(cpu) {
tasklet_kill(hv_context.event_dpc[cpu]);
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index eaf2f91..45cef3d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -969,7 +969,6 @@ config SENSORS_LM73
config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles"
depends on I2C
- depends on THERMAL || !THERMAL_OF
select REGMAP_I2C
help
If you say yes here you get support for one common type of
@@ -1119,6 +1118,7 @@ config SENSORS_LM95241
config SENSORS_LM95245
tristate "National Semiconductor LM95245 and compatibles"
depends on I2C
+ select REGMAP_I2C
help
If you say yes here you get support for LM95235 and LM95245
temperature sensor chips.
@@ -1572,7 +1572,6 @@ config SENSORS_THMC50
config SENSORS_TMP102
tristate "Texas Instruments TMP102"
depends on I2C
- depends on THERMAL || !THERMAL_OF
select REGMAP_I2C
help
If you say yes here you get support for Texas Instruments TMP102
@@ -1823,6 +1822,13 @@ config SENSORS_ULTRA45
This driver provides support for the Ultra45 workstation environmental
sensors.
+config SENSORS_XGENE
+ tristate "APM X-Gene SoC hardware monitoring driver"
+ depends on XGENE_SLIMPRO_MBOX || PCC
+ help
+ If you say yes here you get support for the temperature
+ and power sensors for APM X-Gene SoC.
+
if ACPI
comment "ACPI drivers"
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index fe87d28..aecf4ba 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
obj-$(CONFIG_PMBUS) += pmbus/
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index fc1e65a..812fbc0 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -7,8 +7,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * TODO: SPI, support for external temperature sensor
- * use power-down mode for suspend?, interrupt handling?
+ * TODO: SPI, use power-down mode for suspend?, interrupt handling?
*/
#include <linux/kernel.h>
@@ -31,6 +30,7 @@
#define ADT7411_REG_CFG1 0x18
#define ADT7411_CFG1_START_MONITOR (1 << 0)
#define ADT7411_CFG1_RESERVED_BIT1 (1 << 1)
+#define ADT7411_CFG1_EXT_TDM (1 << 2)
#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3)
#define ADT7411_REG_CFG2 0x19
@@ -57,6 +57,7 @@ struct adt7411_data {
unsigned long next_update;
int vref_cached;
struct i2c_client *client;
+ bool use_ext_temp;
};
/*
@@ -127,11 +128,20 @@ static ssize_t adt7411_show_vdd(struct device *dev,
static ssize_t adt7411_show_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ int nr = to_sensor_dev_attr(attr)->index;
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
- ADT7411_REG_INT_TEMP_MSB, 0);
-
+ int val;
+ struct {
+ u8 low;
+ u8 high;
+ } reg[2] = {
+ { ADT7411_REG_INT_TEMP_VDD_LSB, ADT7411_REG_INT_TEMP_MSB },
+ { ADT7411_REG_EXT_TEMP_AIN14_LSB,
+ ADT7411_REG_EXT_TEMP_AIN1_MSB },
+ };
+
+ val = adt7411_read_10_bit(client, reg[nr].low, reg[nr].high, 0);
if (val < 0)
return val;
@@ -218,11 +228,13 @@ static ssize_t adt7411_set_bit(struct device *dev,
return ret < 0 ? ret : count;
}
+
#define ADT7411_BIT_ATTR(__name, __reg, __bit) \
SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \
adt7411_set_bit, __bit, __reg)
-static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, adt7411_show_temp, NULL, 1);
static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1);
@@ -237,7 +249,8 @@ static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_22
static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD);
static struct attribute *adt7411_attrs[] = {
- &dev_attr_temp1_input.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
&dev_attr_in0_input.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
@@ -253,7 +266,27 @@ static struct attribute *adt7411_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(adt7411);
+static umode_t adt7411_attrs_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ bool visible = true;
+
+ if (attr == &sensor_dev_attr_temp2_input.dev_attr.attr)
+ visible = data->use_ext_temp;
+ else if (attr == &sensor_dev_attr_in1_input.dev_attr.attr ||
+ attr == &sensor_dev_attr_in2_input.dev_attr.attr)
+ visible = !data->use_ext_temp;
+
+ return visible ? attr->mode : 0;
+}
+
+static const struct attribute_group adt7411_group = {
+ .attrs = adt7411_attrs,
+ .is_visible = adt7411_attrs_visible,
+};
+__ATTRIBUTE_GROUPS(adt7411);
static int adt7411_detect(struct i2c_client *client,
struct i2c_board_info *info)
@@ -309,6 +342,8 @@ static int adt7411_init_device(struct adt7411_data *data)
if (ret < 0)
return ret;
+ data->use_ext_temp = ret & ADT7411_CFG1_EXT_TDM;
+
/*
* We must only write zero to bit 1 and only one to bit 3 according to
* the datasheet.
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index f5da39a..6e60ca5 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -32,6 +32,7 @@
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/slab.h>
+#include <linux/util_macros.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
@@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
+#define ADT7470_REG_CFG_2 0x74
#define ADT7470_REG_ACOUSTICS12 0x75
#define ADT7470_REG_ACOUSTICS34 0x76
#define ADT7470_REG_DEVICE 0x3D
@@ -142,6 +144,11 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define FAN_PERIOD_INVALID 65535
#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+/* Config registers 1 and 2 include fields for selecting the PWM frequency */
+#define ADT7470_CFG_LF 0x40
+#define ADT7470_FREQ_MASK 0x70
+#define ADT7470_FREQ_SHIFT 4
+
struct adt7470_data {
struct i2c_client *client;
struct mutex lock;
@@ -170,7 +177,6 @@ struct adt7470_data {
u8 pwm_auto_temp[ADT7470_PWM_COUNT];
struct task_struct *auto_update;
- struct completion auto_update_stop;
unsigned int auto_update_interval;
};
@@ -266,12 +272,14 @@ static int adt7470_update_thread(void *p)
mutex_lock(&data->lock);
adt7470_read_temperatures(client, data);
mutex_unlock(&data->lock);
+
+ set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
break;
- msleep_interruptible(data->auto_update_interval);
+
+ schedule_timeout(msecs_to_jiffies(data->auto_update_interval));
}
- complete_all(&data->auto_update_stop);
return 0;
}
@@ -538,6 +546,28 @@ static ssize_t show_alarm_mask(struct device *dev,
return sprintf(buf, "%x\n", data->alarms_mask);
}
+static ssize_t set_alarm_mask(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ long mask;
+
+ if (kstrtoul(buf, 0, &mask))
+ return -EINVAL;
+
+ if (mask & ~0xffff)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ data->alarms_mask = mask;
+ adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
static ssize_t show_fan_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
@@ -688,6 +718,70 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
return count;
}
+/* These are the valid PWM frequencies to the nearest Hz */
+static const int adt7470_freq_map[] = {
+ 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+};
+
+static ssize_t show_pwm_freq(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7470_data *data = adt7470_update_device(dev);
+ unsigned char cfg_reg_1;
+ unsigned char cfg_reg_2;
+ int index;
+
+ mutex_lock(&data->lock);
+ cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
+ cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
+ mutex_unlock(&data->lock);
+
+ index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
+ if (!(cfg_reg_1 & ADT7470_CFG_LF))
+ index += 8;
+ if (index >= ARRAY_SIZE(adt7470_freq_map))
+ index = ARRAY_SIZE(adt7470_freq_map) - 1;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
+}
+
+static ssize_t set_pwm_freq(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long freq;
+ int index;
+ int low_freq = ADT7470_CFG_LF;
+ unsigned char val;
+
+ if (kstrtol(buf, 10, &freq))
+ return -EINVAL;
+
+ /* Round the user value given to the closest available frequency */
+ index = find_closest(freq, adt7470_freq_map,
+ ARRAY_SIZE(adt7470_freq_map));
+
+ if (index >= 8) {
+ index -= 8;
+ low_freq = 0;
+ }
+
+ mutex_lock(&data->lock);
+ /* Configuration Register 1 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
+ (val & ~ADT7470_CFG_LF) | low_freq);
+ /* Configuration Register 2 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
+ (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT));
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
static ssize_t show_pwm_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
@@ -918,7 +1012,8 @@ static ssize_t show_alarm(struct device *dev,
return sprintf(buf, "0\n");
}
-static DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarm_mask, NULL);
+static DEVICE_ATTR(alarm_mask, S_IWUSR | S_IRUGO, show_alarm_mask,
+ set_alarm_mask);
static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors,
set_num_temp_sensors);
static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO,
@@ -1038,6 +1133,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
+static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
+
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm_min, set_pwm_min, 0);
static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
@@ -1154,6 +1251,7 @@ static struct attribute *adt7470_attrs[] = {
&sensor_dev_attr_fan4_alarm.dev_attr.attr,
&sensor_dev_attr_force_pwm_max.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
+ &dev_attr_pwm1_freq.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
&sensor_dev_attr_pwm4.dev_attr.attr,
@@ -1256,7 +1354,6 @@ static int adt7470_probe(struct i2c_client *client,
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
- init_completion(&data->auto_update_stop);
data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
dev_name(hwmon_dev));
if (IS_ERR(data->auto_update)) {
@@ -1271,7 +1368,6 @@ static int adt7470_remove(struct i2c_client *client)
struct adt7470_data *data = i2c_get_clientdata(client);
kthread_stop(data->auto_update);
- wait_for_completion(&data->auto_update_stop);
return 0;
}
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index acf9c03..34704b0 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -21,6 +21,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -36,6 +37,7 @@
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/ctype.h>
+#include <linux/smp.h>
#include <linux/i8k.h>
@@ -134,11 +136,11 @@ static inline const char *i8k_get_dmi_data(int field)
/*
* Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
*/
-static int i8k_smm(struct smm_regs *regs)
+static int i8k_smm_func(void *par)
{
int rc;
+ struct smm_regs *regs = par;
int eax = regs->eax;
- cpumask_var_t old_mask;
#ifdef DEBUG
int ebx = regs->ebx;
@@ -149,16 +151,8 @@ static int i8k_smm(struct smm_regs *regs)
#endif
/* SMM requires CPU 0 */
- if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
- return -ENOMEM;
- cpumask_copy(old_mask, &current->cpus_allowed);
- rc = set_cpus_allowed_ptr(current, cpumask_of(0));
- if (rc)
- goto out;
- if (smp_processor_id() != 0) {
- rc = -EBUSY;
- goto out;
- }
+ if (smp_processor_id() != 0)
+ return -EBUSY;
#if defined(CONFIG_X86_64)
asm volatile("pushq %%rax\n\t"
@@ -216,10 +210,6 @@ static int i8k_smm(struct smm_regs *regs)
if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
rc = -EINVAL;
-out:
- set_cpus_allowed_ptr(current, old_mask);
- free_cpumask_var(old_mask);
-
#ifdef DEBUG
rettime = ktime_get();
delta = ktime_sub(rettime, calltime);
@@ -232,6 +222,20 @@ out:
}
/*
+ * Call the System Management Mode BIOS.
+ */
+static int i8k_smm(struct smm_regs *regs)
+{
+ int ret;
+
+ get_online_cpus();
+ ret = smp_call_on_cpu(0, i8k_smm_func, regs, true);
+ put_online_cpus();
+
+ return ret;
+}
+
+/*
* Read the fan status.
*/
static int i8k_get_fan_status(int fan)
diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c
index 48633e5..0f0277e 100644
--- a/drivers/hwmon/ftsteutates.c
+++ b/drivers/hwmon/ftsteutates.c
@@ -36,6 +36,10 @@
#define FTS_EVENT_STATUS_REG 0x0006
#define FTS_GLOBAL_CONTROL_REG 0x0007
+#define FTS_DEVICE_DETECT_REG_1 0x0C
+#define FTS_DEVICE_DETECT_REG_2 0x0D
+#define FTS_DEVICE_DETECT_REG_3 0x0E
+
#define FTS_SENSOR_EVENT_REG 0x0010
#define FTS_FAN_EVENT_REG 0x0014
@@ -54,6 +58,8 @@
#define FTS_NO_TEMP_SENSORS 0x10
#define FTS_NO_VOLT_SENSORS 0x04
+static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
+
static struct i2c_device_id fts_id[] = {
{ "ftsteutates", 0 },
{ }
@@ -734,6 +740,42 @@ static const struct attribute_group *fts_attr_groups[] = {
/*****************************************************************************/
/* Module initialization / remove functions */
/*****************************************************************************/
+static int fts_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int val;
+
+ /* detection works with revsion greater or equal to 0x2b */
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
+ if (val < 0x2b)
+ return -ENODEV;
+
+ /* Device Detect Regs must have 0x17 0x34 and 0x54 */
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1);
+ if (val != 0x17)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2);
+ if (val != 0x34)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3);
+ if (val != 0x54)
+ return -ENODEV;
+
+ /*
+ * 0x10 == Baseboard Management Controller, 0x01 == Teutates
+ * Device ID Reg needs to be 0x11
+ */
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
+ if (val != 0x11)
+ return -ENODEV;
+
+ strlcpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
+ info->flags = 0;
+ return 0;
+}
+
static int fts_remove(struct i2c_client *client)
{
struct fts_data *data = dev_get_drvdata(&client->dev);
@@ -804,12 +846,15 @@ static int fts_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Module Details */
/*****************************************************************************/
static struct i2c_driver fts_driver = {
+ .class = I2C_CLASS_HWMON,
.driver = {
.name = "ftsteutates",
},
.id_table = fts_id,
.probe = fts_probe,
.remove = fts_remove,
+ .detect = fts_detect,
+ .address_list = normal_i2c,
};
module_i2c_driver(fts_driver);
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index a26c385..adae684 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -12,17 +12,17 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
+#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/kdev_t.h>
-#include <linux/idr.h>
-#include <linux/hwmon.h>
#include <linux/gfp.h>
-#include <linux/spinlock.h>
+#include <linux/hwmon.h>
+#include <linux/idr.h>
+#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/thermal.h>
#define HWMON_ID_PREFIX "hwmon"
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
@@ -30,9 +30,35 @@
struct hwmon_device {
const char *name;
struct device dev;
+ const struct hwmon_chip_info *chip;
+
+ struct attribute_group group;
+ const struct attribute_group **groups;
};
+
#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev)
+struct hwmon_device_attribute {
+ struct device_attribute dev_attr;
+ const struct hwmon_ops *ops;
+ enum hwmon_sensor_types type;
+ u32 attr;
+ int index;
+};
+
+#define to_hwmon_attr(d) \
+ container_of(d, struct hwmon_device_attribute, dev_attr)
+
+/*
+ * Thermal zone information
+ * In addition to the reference to the hwmon device,
+ * also provides the sensor index.
+ */
+struct hwmon_thermal_data {
+ struct hwmon_device *hwdev; /* Reference to hwmon device */
+ int index; /* sensor index */
+};
+
static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -80,25 +106,409 @@ static struct class hwmon_class = {
static DEFINE_IDA(hwmon_ida);
-/**
- * hwmon_device_register_with_groups - register w/ hwmon
- * @dev: the parent device
- * @name: hwmon name attribute
- * @drvdata: driver data to attach to created device
- * @groups: List of attribute groups to create
- *
- * hwmon_device_unregister() must be called when the device is no
- * longer needed.
- *
- * Returns the pointer to the new device.
+/* Thermal zone handling */
+
+/*
+ * The complex conditional is necessary to avoid a cyclic dependency
+ * between hwmon and thermal_sys modules.
*/
-struct device *
-hwmon_device_register_with_groups(struct device *dev, const char *name,
- void *drvdata,
- const struct attribute_group **groups)
+#if IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) && \
+ (!defined(CONFIG_THERMAL_HWMON) || \
+ !(defined(MODULE) && IS_MODULE(CONFIG_THERMAL)))
+static int hwmon_thermal_get_temp(void *data, int *temp)
+{
+ struct hwmon_thermal_data *tdata = data;
+ struct hwmon_device *hwdev = tdata->hwdev;
+ int ret;
+ long t;
+
+ ret = hwdev->chip->ops->read(&hwdev->dev, hwmon_temp, hwmon_temp_input,
+ tdata->index, &t);
+ if (ret < 0)
+ return ret;
+
+ *temp = t;
+
+ return 0;
+}
+
+static struct thermal_zone_of_device_ops hwmon_thermal_ops = {
+ .get_temp = hwmon_thermal_get_temp,
+};
+
+static int hwmon_thermal_add_sensor(struct device *dev,
+ struct hwmon_device *hwdev, int index)
+{
+ struct hwmon_thermal_data *tdata;
+
+ tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
+ if (!tdata)
+ return -ENOMEM;
+
+ tdata->hwdev = hwdev;
+ tdata->index = index;
+
+ devm_thermal_zone_of_sensor_register(&hwdev->dev, index, tdata,
+ &hwmon_thermal_ops);
+
+ return 0;
+}
+#else
+static int hwmon_thermal_add_sensor(struct device *dev,
+ struct hwmon_device *hwdev, int index)
+{
+ return 0;
+}
+#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
+
+/* sysfs attribute management */
+
+static ssize_t hwmon_attr_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
+ long val;
+ int ret;
+
+ ret = hattr->ops->read(dev, hattr->type, hattr->attr, hattr->index,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t hwmon_attr_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = hattr->ops->write(dev, hattr->type, hattr->attr, hattr->index,
+ val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static int hwmon_attr_base(enum hwmon_sensor_types type)
+{
+ if (type == hwmon_in)
+ return 0;
+ return 1;
+}
+
+static struct attribute *hwmon_genattr(struct device *dev,
+ const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr,
+ int index,
+ const char *template,
+ const struct hwmon_ops *ops)
+{
+ struct hwmon_device_attribute *hattr;
+ struct device_attribute *dattr;
+ struct attribute *a;
+ umode_t mode;
+ char *name;
+
+ /* The attribute is invisible if there is no template string */
+ if (!template)
+ return ERR_PTR(-ENOENT);
+
+ mode = ops->is_visible(drvdata, type, attr, index);
+ if (!mode)
+ return ERR_PTR(-ENOENT);
+
+ if ((mode & S_IRUGO) && !ops->read)
+ return ERR_PTR(-EINVAL);
+ if ((mode & S_IWUGO) && !ops->write)
+ return ERR_PTR(-EINVAL);
+
+ if (type == hwmon_chip) {
+ name = (char *)template;
+ } else {
+ name = devm_kzalloc(dev, strlen(template) + 16, GFP_KERNEL);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+ scnprintf(name, strlen(template) + 16, template,
+ index + hwmon_attr_base(type));
+ }
+
+ hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL);
+ if (!hattr)
+ return ERR_PTR(-ENOMEM);
+
+ hattr->type = type;
+ hattr->attr = attr;
+ hattr->index = index;
+ hattr->ops = ops;
+
+ dattr = &hattr->dev_attr;
+ dattr->show = hwmon_attr_show;
+ dattr->store = hwmon_attr_store;
+
+ a = &dattr->attr;
+ sysfs_attr_init(a);
+ a->name = name;
+ a->mode = mode;
+
+ return a;
+}
+
+static const char * const hwmon_chip_attr_templates[] = {
+ [hwmon_chip_temp_reset_history] = "temp_reset_history",
+ [hwmon_chip_in_reset_history] = "in_reset_history",
+ [hwmon_chip_curr_reset_history] = "curr_reset_history",
+ [hwmon_chip_power_reset_history] = "power_reset_history",
+ [hwmon_chip_update_interval] = "update_interval",
+ [hwmon_chip_alarms] = "alarms",
+};
+
+static const char * const hwmon_temp_attr_templates[] = {
+ [hwmon_temp_input] = "temp%d_input",
+ [hwmon_temp_type] = "temp%d_type",
+ [hwmon_temp_lcrit] = "temp%d_lcrit",
+ [hwmon_temp_lcrit_hyst] = "temp%d_lcrit_hyst",
+ [hwmon_temp_min] = "temp%d_min",
+ [hwmon_temp_min_hyst] = "temp%d_min_hyst",
+ [hwmon_temp_max] = "temp%d_max",
+ [hwmon_temp_max_hyst] = "temp%d_max_hyst",
+ [hwmon_temp_crit] = "temp%d_crit",
+ [hwmon_temp_crit_hyst] = "temp%d_crit_hyst",
+ [hwmon_temp_emergency] = "temp%d_emergency",
+ [hwmon_temp_emergency_hyst] = "temp%d_emergency_hyst",
+ [hwmon_temp_alarm] = "temp%d_alarm",
+ [hwmon_temp_lcrit_alarm] = "temp%d_lcrit_alarm",
+ [hwmon_temp_min_alarm] = "temp%d_min_alarm",
+ [hwmon_temp_max_alarm] = "temp%d_max_alarm",
+ [hwmon_temp_crit_alarm] = "temp%d_crit_alarm",
+ [hwmon_temp_emergency_alarm] = "temp%d_emergency_alarm",
+ [hwmon_temp_fault] = "temp%d_fault",
+ [hwmon_temp_offset] = "temp%d_offset",
+ [hwmon_temp_label] = "temp%d_label",
+ [hwmon_temp_lowest] = "temp%d_lowest",
+ [hwmon_temp_highest] = "temp%d_highest",
+ [hwmon_temp_reset_history] = "temp%d_reset_history",
+};
+
+static const char * const hwmon_in_attr_templates[] = {
+ [hwmon_in_input] = "in%d_input",
+ [hwmon_in_min] = "in%d_min",
+ [hwmon_in_max] = "in%d_max",
+ [hwmon_in_lcrit] = "in%d_lcrit",
+ [hwmon_in_crit] = "in%d_crit",
+ [hwmon_in_average] = "in%d_average",
+ [hwmon_in_lowest] = "in%d_lowest",
+ [hwmon_in_highest] = "in%d_highest",
+ [hwmon_in_reset_history] = "in%d_reset_history",
+ [hwmon_in_label] = "in%d_label",
+ [hwmon_in_alarm] = "in%d_alarm",
+ [hwmon_in_min_alarm] = "in%d_min_alarm",
+ [hwmon_in_max_alarm] = "in%d_max_alarm",
+ [hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm",
+ [hwmon_in_crit_alarm] = "in%d_crit_alarm",
+};
+
+static const char * const hwmon_curr_attr_templates[] = {
+ [hwmon_curr_input] = "curr%d_input",
+ [hwmon_curr_min] = "curr%d_min",
+ [hwmon_curr_max] = "curr%d_max",
+ [hwmon_curr_lcrit] = "curr%d_lcrit",
+ [hwmon_curr_crit] = "curr%d_crit",
+ [hwmon_curr_average] = "curr%d_average",
+ [hwmon_curr_lowest] = "curr%d_lowest",
+ [hwmon_curr_highest] = "curr%d_highest",
+ [hwmon_curr_reset_history] = "curr%d_reset_history",
+ [hwmon_curr_label] = "curr%d_label",
+ [hwmon_curr_alarm] = "curr%d_alarm",
+ [hwmon_curr_min_alarm] = "curr%d_min_alarm",
+ [hwmon_curr_max_alarm] = "curr%d_max_alarm",
+ [hwmon_curr_lcrit_alarm] = "curr%d_lcrit_alarm",
+ [hwmon_curr_crit_alarm] = "curr%d_crit_alarm",
+};
+
+static const char * const hwmon_power_attr_templates[] = {
+ [hwmon_power_average] = "power%d_average",
+ [hwmon_power_average_interval] = "power%d_average_interval",
+ [hwmon_power_average_interval_max] = "power%d_interval_max",
+ [hwmon_power_average_interval_min] = "power%d_interval_min",
+ [hwmon_power_average_highest] = "power%d_average_highest",
+ [hwmon_power_average_lowest] = "power%d_average_lowest",
+ [hwmon_power_average_max] = "power%d_average_max",
+ [hwmon_power_average_min] = "power%d_average_min",
+ [hwmon_power_input] = "power%d_input",
+ [hwmon_power_input_highest] = "power%d_input_highest",
+ [hwmon_power_input_lowest] = "power%d_input_lowest",
+ [hwmon_power_reset_history] = "power%d_reset_history",
+ [hwmon_power_accuracy] = "power%d_accuracy",
+ [hwmon_power_cap] = "power%d_cap",
+ [hwmon_power_cap_hyst] = "power%d_cap_hyst",
+ [hwmon_power_cap_max] = "power%d_cap_max",
+ [hwmon_power_cap_min] = "power%d_cap_min",
+ [hwmon_power_max] = "power%d_max",
+ [hwmon_power_crit] = "power%d_crit",
+ [hwmon_power_label] = "power%d_label",
+ [hwmon_power_alarm] = "power%d_alarm",
+ [hwmon_power_cap_alarm] = "power%d_cap_alarm",
+ [hwmon_power_max_alarm] = "power%d_max_alarm",
+ [hwmon_power_crit_alarm] = "power%d_crit_alarm",
+};
+
+static const char * const hwmon_energy_attr_templates[] = {
+ [hwmon_energy_input] = "energy%d_input",
+ [hwmon_energy_label] = "energy%d_label",
+};
+
+static const char * const hwmon_humidity_attr_templates[] = {
+ [hwmon_humidity_input] = "humidity%d_input",
+ [hwmon_humidity_label] = "humidity%d_label",
+ [hwmon_humidity_min] = "humidity%d_min",
+ [hwmon_humidity_min_hyst] = "humidity%d_min_hyst",
+ [hwmon_humidity_max] = "humidity%d_max",
+ [hwmon_humidity_max_hyst] = "humidity%d_max_hyst",
+ [hwmon_humidity_alarm] = "humidity%d_alarm",
+ [hwmon_humidity_fault] = "humidity%d_fault",
+};
+
+static const char * const hwmon_fan_attr_templates[] = {
+ [hwmon_fan_input] = "fan%d_input",
+ [hwmon_fan_label] = "fan%d_label",
+ [hwmon_fan_min] = "fan%d_min",
+ [hwmon_fan_max] = "fan%d_max",
+ [hwmon_fan_div] = "fan%d_div",
+ [hwmon_fan_pulses] = "fan%d_pulses",
+ [hwmon_fan_target] = "fan%d_target",
+ [hwmon_fan_alarm] = "fan%d_alarm",
+ [hwmon_fan_min_alarm] = "fan%d_min_alarm",
+ [hwmon_fan_max_alarm] = "fan%d_max_alarm",
+ [hwmon_fan_fault] = "fan%d_fault",
+};
+
+static const char * const hwmon_pwm_attr_templates[] = {
+ [hwmon_pwm_input] = "pwm%d",
+ [hwmon_pwm_enable] = "pwm%d_enable",
+ [hwmon_pwm_mode] = "pwm%d_mode",
+ [hwmon_pwm_freq] = "pwm%d_freq",
+};
+
+static const char * const *__templates[] = {
+ [hwmon_chip] = hwmon_chip_attr_templates,
+ [hwmon_temp] = hwmon_temp_attr_templates,
+ [hwmon_in] = hwmon_in_attr_templates,
+ [hwmon_curr] = hwmon_curr_attr_templates,
+ [hwmon_power] = hwmon_power_attr_templates,
+ [hwmon_energy] = hwmon_energy_attr_templates,
+ [hwmon_humidity] = hwmon_humidity_attr_templates,
+ [hwmon_fan] = hwmon_fan_attr_templates,
+ [hwmon_pwm] = hwmon_pwm_attr_templates,
+};
+
+static const int __templates_size[] = {
+ [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attr_templates),
+ [hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates),
+ [hwmon_in] = ARRAY_SIZE(hwmon_in_attr_templates),
+ [hwmon_curr] = ARRAY_SIZE(hwmon_curr_attr_templates),
+ [hwmon_power] = ARRAY_SIZE(hwmon_power_attr_templates),
+ [hwmon_energy] = ARRAY_SIZE(hwmon_energy_attr_templates),
+ [hwmon_humidity] = ARRAY_SIZE(hwmon_humidity_attr_templates),
+ [hwmon_fan] = ARRAY_SIZE(hwmon_fan_attr_templates),
+ [hwmon_pwm] = ARRAY_SIZE(hwmon_pwm_attr_templates),
+};
+
+static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info)
+{
+ int i, n;
+
+ for (i = n = 0; info->config[i]; i++)
+ n += hweight32(info->config[i]);
+
+ return n;
+}
+
+static int hwmon_genattrs(struct device *dev,
+ const void *drvdata,
+ struct attribute **attrs,
+ const struct hwmon_ops *ops,
+ const struct hwmon_channel_info *info)
+{
+ const char * const *templates;
+ int template_size;
+ int i, aindex = 0;
+
+ if (info->type >= ARRAY_SIZE(__templates))
+ return -EINVAL;
+
+ templates = __templates[info->type];
+ template_size = __templates_size[info->type];
+
+ for (i = 0; info->config[i]; i++) {
+ u32 attr_mask = info->config[i];
+ u32 attr;
+
+ while (attr_mask) {
+ struct attribute *a;
+
+ attr = __ffs(attr_mask);
+ attr_mask &= ~BIT(attr);
+ if (attr >= template_size)
+ return -EINVAL;
+ a = hwmon_genattr(dev, drvdata, info->type, attr, i,
+ templates[attr], ops);
+ if (IS_ERR(a)) {
+ if (PTR_ERR(a) != -ENOENT)
+ return PTR_ERR(a);
+ continue;
+ }
+ attrs[aindex++] = a;
+ }
+ }
+ return aindex;
+}
+
+static struct attribute **
+__hwmon_create_attrs(struct device *dev, const void *drvdata,
+ const struct hwmon_chip_info *chip)
+{
+ int ret, i, aindex = 0, nattrs = 0;
+ struct attribute **attrs;
+
+ for (i = 0; chip->info[i]; i++)
+ nattrs += hwmon_num_channel_attrs(chip->info[i]);
+
+ if (nattrs == 0)
+ return ERR_PTR(-EINVAL);
+
+ attrs = devm_kcalloc(dev, nattrs + 1, sizeof(*attrs), GFP_KERNEL);
+ if (!attrs)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; chip->info[i]; i++) {
+ ret = hwmon_genattrs(dev, drvdata, &attrs[aindex], chip->ops,
+ chip->info[i]);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ aindex += ret;
+ }
+
+ return attrs;
+}
+
+static struct device *
+__hwmon_device_register(struct device *dev, const char *name, void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **groups)
{
struct hwmon_device *hwdev;
- int err, id;
+ struct device *hdev;
+ int i, j, err, id;
/* Do not accept invalid characters in hwmon name attribute */
if (name && (!strlen(name) || strpbrk(name, "-* \t\n")))
@@ -114,28 +524,128 @@ hwmon_device_register_with_groups(struct device *dev, const char *name,
goto ida_remove;
}
+ hdev = &hwdev->dev;
+
+ if (chip && chip->ops->is_visible) {
+ struct attribute **attrs;
+ int ngroups = 2;
+
+ if (groups)
+ for (i = 0; groups[i]; i++)
+ ngroups++;
+
+ hwdev->groups = devm_kcalloc(dev, ngroups, sizeof(*groups),
+ GFP_KERNEL);
+ if (!hwdev->groups)
+ return ERR_PTR(-ENOMEM);
+
+ attrs = __hwmon_create_attrs(dev, drvdata, chip);
+ if (IS_ERR(attrs)) {
+ err = PTR_ERR(attrs);
+ goto free_hwmon;
+ }
+
+ hwdev->group.attrs = attrs;
+ ngroups = 0;
+ hwdev->groups[ngroups++] = &hwdev->group;
+
+ if (groups) {
+ for (i = 0; groups[i]; i++)
+ hwdev->groups[ngroups++] = groups[i];
+ }
+
+ hdev->groups = hwdev->groups;
+ } else {
+ hdev->groups = groups;
+ }
+
hwdev->name = name;
- hwdev->dev.class = &hwmon_class;
- hwdev->dev.parent = dev;
- hwdev->dev.groups = groups;
- hwdev->dev.of_node = dev ? dev->of_node : NULL;
- dev_set_drvdata(&hwdev->dev, drvdata);
- dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id);
- err = device_register(&hwdev->dev);
+ hdev->class = &hwmon_class;
+ hdev->parent = dev;
+ hdev->of_node = dev ? dev->of_node : NULL;
+ hwdev->chip = chip;
+ dev_set_drvdata(hdev, drvdata);
+ dev_set_name(hdev, HWMON_ID_FORMAT, id);
+ err = device_register(hdev);
if (err)
- goto free;
+ goto free_hwmon;
+
+ if (chip && chip->ops->is_visible && chip->ops->read &&
+ chip->info[0]->type == hwmon_chip &&
+ (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
+ const struct hwmon_channel_info **info = chip->info;
+
+ for (i = 1; info[i]; i++) {
+ if (info[i]->type != hwmon_temp)
+ continue;
+
+ for (j = 0; info[i]->config[j]; j++) {
+ if (!chip->ops->is_visible(drvdata, hwmon_temp,
+ hwmon_temp_input, j))
+ continue;
+ if (info[i]->config[j] & HWMON_T_INPUT)
+ hwmon_thermal_add_sensor(dev, hwdev, j);
+ }
+ }
+ }
- return &hwdev->dev;
+ return hdev;
-free:
+free_hwmon:
kfree(hwdev);
ida_remove:
ida_simple_remove(&hwmon_ida, id);
return ERR_PTR(err);
}
+
+/**
+ * hwmon_device_register_with_groups - register w/ hwmon
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ * @groups: List of attribute groups to create
+ *
+ * hwmon_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *
+hwmon_device_register_with_groups(struct device *dev, const char *name,
+ void *drvdata,
+ const struct attribute_group **groups)
+{
+ return __hwmon_device_register(dev, name, drvdata, NULL, groups);
+}
EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
/**
+ * hwmon_device_register_with_info - register w/ hwmon
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ * @info: Pointer to hwmon chip information
+ * @groups - pointer to list of driver specific attribute groups
+ *
+ * hwmon_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *
+hwmon_device_register_with_info(struct device *dev, const char *name,
+ void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **groups)
+{
+ if (chip && (!chip->ops || !chip->info))
+ return ERR_PTR(-EINVAL);
+
+ return __hwmon_device_register(dev, name, drvdata, chip, groups);
+}
+EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
+
+/**
* hwmon_device_register - register w/ hwmon
* @dev: the device to register
*
@@ -213,6 +723,48 @@ error:
}
EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
+/**
+ * devm_hwmon_device_register_with_info - register w/ hwmon
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ * @info: Pointer to hwmon chip information
+ * @groups - pointer to list of driver specific attribute groups
+ *
+ * Returns the pointer to the new device. The new device is automatically
+ * unregistered with the parent device.
+ */
+struct device *
+devm_hwmon_device_register_with_info(struct device *dev, const char *name,
+ void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **groups)
+{
+ struct device **ptr, *hwdev;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ hwdev = hwmon_device_register_with_info(dev, name, drvdata, chip,
+ groups);
+ if (IS_ERR(hwdev))
+ goto error;
+
+ *ptr = hwdev;
+ devres_add(dev, ptr);
+
+ return hwdev;
+
+error:
+ devres_free(ptr);
+ return hwdev;
+}
+EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info);
+
static int devm_hwmon_match(struct device *dev, void *res, void *data)
{
struct device **hwdev = res;
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
index 55b5a8f..6d2e660 100644
--- a/drivers/hwmon/ibmpowernv.c
+++ b/drivers/hwmon/ibmpowernv.c
@@ -143,13 +143,11 @@ static void __init make_sensor_label(struct device_node *np,
if (cpuid >= 0)
/*
* The digital thermal sensors are associated
- * with a core. Let's print out the range of
- * cpu ids corresponding to the hardware
- * threads of the core.
+ * with a core.
*/
n += snprintf(sdata->label + n,
- sizeof(sdata->label) - n, " %d-%d",
- cpuid, cpuid + threads_per_core - 1);
+ sizeof(sdata->label) - n, " %d",
+ cpuid);
else
n += snprintf(sdata->label + n,
sizeof(sdata->label) - n, " phy%d", id);
diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c
index 8944987..f6a7667 100644
--- a/drivers/hwmon/iio_hwmon.c
+++ b/drivers/hwmon/iio_hwmon.c
@@ -73,8 +73,11 @@ static int iio_hwmon_probe(struct platform_device *pdev)
name = dev->of_node->name;
channels = iio_channel_get_all(dev);
- if (IS_ERR(channels))
+ if (IS_ERR(channels)) {
+ if (PTR_ERR(channels) == -ENODEV)
+ return -EPROBE_DEFER;
return PTR_ERR(channels);
+ }
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (st == NULL) {
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 4667012..ad82cb2 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -2011,10 +2011,10 @@ static struct attribute *it87_attributes_in[] = {
&sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */
&sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */
- &sensor_dev_attr_in9_input.dev_attr.attr, /* 41 */
- &sensor_dev_attr_in10_input.dev_attr.attr, /* 41 */
- &sensor_dev_attr_in11_input.dev_attr.attr, /* 41 */
- &sensor_dev_attr_in12_input.dev_attr.attr, /* 41 */
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in12_input.dev_attr.attr,
NULL
};
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index 9d5f85f..1bf22ef 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -28,7 +28,6 @@
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
@@ -254,170 +253,148 @@ abort:
return ret;
}
-/* sysfs functions */
-
-static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
- char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct jc42_data *data = jc42_update_device(dev);
- if (IS_ERR(data))
- return PTR_ERR(data);
- return sprintf(buf, "%d\n",
- jc42_temp_from_reg(data->temp[attr->index]));
-}
-
-static ssize_t show_temp_hyst(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct jc42_data *data = jc42_update_device(dev);
int temp, hyst;
if (IS_ERR(data))
return PTR_ERR(data);
- temp = jc42_temp_from_reg(data->temp[attr->index]);
- hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
- >> JC42_CFG_HYST_SHIFT];
- return sprintf(buf, "%d\n", temp - hyst);
+ switch (attr) {
+ case hwmon_temp_input:
+ *val = jc42_temp_from_reg(data->temp[t_input]);
+ return 0;
+ case hwmon_temp_min:
+ *val = jc42_temp_from_reg(data->temp[t_min]);
+ return 0;
+ case hwmon_temp_max:
+ *val = jc42_temp_from_reg(data->temp[t_max]);
+ return 0;
+ case hwmon_temp_crit:
+ *val = jc42_temp_from_reg(data->temp[t_crit]);
+ return 0;
+ case hwmon_temp_max_hyst:
+ temp = jc42_temp_from_reg(data->temp[t_max]);
+ hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+ >> JC42_CFG_HYST_SHIFT];
+ *val = temp - hyst;
+ return 0;
+ case hwmon_temp_crit_hyst:
+ temp = jc42_temp_from_reg(data->temp[t_crit]);
+ hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+ >> JC42_CFG_HYST_SHIFT];
+ *val = temp - hyst;
+ return 0;
+ case hwmon_temp_min_alarm:
+ *val = (data->temp[t_input] >> JC42_ALARM_MIN_BIT) & 1;
+ return 0;
+ case hwmon_temp_max_alarm:
+ *val = (data->temp[t_input] >> JC42_ALARM_MAX_BIT) & 1;
+ return 0;
+ case hwmon_temp_crit_alarm:
+ *val = (data->temp[t_input] >> JC42_ALARM_CRIT_BIT) & 1;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct jc42_data *data = dev_get_drvdata(dev);
- int err, ret = count;
- int nr = attr->index;
- long val;
+ struct i2c_client *client = data->client;
+ int diff, hyst;
+ int ret;
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
mutex_lock(&data->update_lock);
- data->temp[nr] = jc42_temp_to_reg(val, data->extended);
- err = i2c_smbus_write_word_swapped(data->client, temp_regs[nr],
- data->temp[nr]);
- if (err < 0)
- ret = err;
- mutex_unlock(&data->update_lock);
- return ret;
-}
-/*
- * JC42.4 compliant chips only support four hysteresis values.
- * Pick best choice and go from there.
- */
-static ssize_t set_temp_crit_hyst(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct jc42_data *data = dev_get_drvdata(dev);
- long val;
- int diff, hyst;
- int err;
- int ret = count;
-
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
-
- val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED :
- JC42_TEMP_MIN) - 6000, JC42_TEMP_MAX);
- diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
-
- hyst = 0;
- if (diff > 0) {
- if (diff < 2250)
- hyst = 1; /* 1.5 degrees C */
- else if (diff < 4500)
- hyst = 2; /* 3.0 degrees C */
- else
- hyst = 3; /* 6.0 degrees C */
+ switch (attr) {
+ case hwmon_temp_min:
+ data->temp[t_min] = jc42_temp_to_reg(val, data->extended);
+ ret = i2c_smbus_write_word_swapped(client, temp_regs[t_min],
+ data->temp[t_min]);
+ break;
+ case hwmon_temp_max:
+ data->temp[t_max] = jc42_temp_to_reg(val, data->extended);
+ ret = i2c_smbus_write_word_swapped(client, temp_regs[t_max],
+ data->temp[t_max]);
+ break;
+ case hwmon_temp_crit:
+ data->temp[t_crit] = jc42_temp_to_reg(val, data->extended);
+ ret = i2c_smbus_write_word_swapped(client, temp_regs[t_crit],
+ data->temp[t_crit]);
+ break;
+ case hwmon_temp_crit_hyst:
+ /*
+ * JC42.4 compliant chips only support four hysteresis values.
+ * Pick best choice and go from there.
+ */
+ val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED
+ : JC42_TEMP_MIN) - 6000,
+ JC42_TEMP_MAX);
+ diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
+ hyst = 0;
+ if (diff > 0) {
+ if (diff < 2250)
+ hyst = 1; /* 1.5 degrees C */
+ else if (diff < 4500)
+ hyst = 2; /* 3.0 degrees C */
+ else
+ hyst = 3; /* 6.0 degrees C */
+ }
+ data->config = (data->config & ~JC42_CFG_HYST_MASK) |
+ (hyst << JC42_CFG_HYST_SHIFT);
+ ret = i2c_smbus_write_word_swapped(data->client,
+ JC42_REG_CONFIG,
+ data->config);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
}
- mutex_lock(&data->update_lock);
- data->config = (data->config & ~JC42_CFG_HYST_MASK)
- | (hyst << JC42_CFG_HYST_SHIFT);
- err = i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
- data->config);
- if (err < 0)
- ret = err;
mutex_unlock(&data->update_lock);
- return ret;
-}
-static ssize_t show_alarm(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- u16 bit = to_sensor_dev_attr(attr)->index;
- struct jc42_data *data = jc42_update_device(dev);
- u16 val;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- val = data->temp[t_input];
- if (bit != JC42_ALARM_CRIT_BIT && (data->config & JC42_CFG_CRIT_ONLY))
- val = 0;
- return sprintf(buf, "%u\n", (val >> bit) & 1);
+ return ret;
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, set_temp, t_crit);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, set_temp, t_min);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, set_temp, t_max);
-
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_hyst,
- set_temp_crit_hyst, t_crit);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max);
-
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
- JC42_ALARM_CRIT_BIT);
-static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
- JC42_ALARM_MIN_BIT);
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
- JC42_ALARM_MAX_BIT);
-
-static struct attribute *jc42_attributes[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_min.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- NULL
-};
-
-static umode_t jc42_attribute_mode(struct kobject *kobj,
- struct attribute *attr, int index)
+static umode_t jc42_is_visible(const void *_data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct jc42_data *data = dev_get_drvdata(dev);
+ const struct jc42_data *data = _data;
unsigned int config = data->config;
- bool readonly;
-
- if (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr)
- readonly = config & JC42_CFG_TCRIT_LOCK;
- else if (attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
- attr == &sensor_dev_attr_temp1_max.dev_attr.attr)
- readonly = config & JC42_CFG_EVENT_LOCK;
- else if (attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)
- readonly = config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK);
- else
- readonly = true;
-
- return S_IRUGO | (readonly ? 0 : S_IWUSR);
+ umode_t mode = S_IRUGO;
+
+ switch (attr) {
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ if (!(config & JC42_CFG_EVENT_LOCK))
+ mode |= S_IWUSR;
+ break;
+ case hwmon_temp_crit:
+ if (!(config & JC42_CFG_TCRIT_LOCK))
+ mode |= S_IWUSR;
+ break;
+ case hwmon_temp_crit_hyst:
+ if (!(config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK)))
+ mode |= S_IWUSR;
+ break;
+ case hwmon_temp_input:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ break;
+ default:
+ mode = 0;
+ break;
+ }
+ return mode;
}
-static const struct attribute_group jc42_group = {
- .attrs = jc42_attributes,
- .is_visible = jc42_attribute_mode,
-};
-__ATTRIBUTE_GROUPS(jc42);
-
/* Return 0 if detection is successful, -ENODEV otherwise */
static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
{
@@ -450,6 +427,34 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
return -ENODEV;
}
+static const u32 jc42_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_MAX_HYST | HWMON_T_CRIT_HYST |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM,
+ 0
+};
+
+static const struct hwmon_channel_info jc42_temp = {
+ .type = hwmon_temp,
+ .config = jc42_temp_config,
+};
+
+static const struct hwmon_channel_info *jc42_info[] = {
+ &jc42_temp,
+ NULL
+};
+
+static const struct hwmon_ops jc42_hwmon_ops = {
+ .is_visible = jc42_is_visible,
+ .read = jc42_read,
+ .write = jc42_write,
+};
+
+static const struct hwmon_chip_info jc42_chip_info = {
+ .ops = &jc42_hwmon_ops,
+ .info = jc42_info,
+};
+
static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
@@ -482,9 +487,9 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
data->config = config;
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data,
- jc42_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &jc42_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index 92f9d4b..eff3b24 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -28,7 +28,6 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/regmap.h>
-#include <linux/thermal.h>
#include "lm75.h"
@@ -88,56 +87,75 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
}
-/* sysfs attributes for hwmon */
-
-static int lm75_read_temp(void *dev, int *temp)
+static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
struct lm75_data *data = dev_get_drvdata(dev);
- unsigned int _temp;
- int err;
-
- err = regmap_read(data->regmap, LM75_REG_TEMP, &_temp);
- if (err < 0)
- return err;
-
- *temp = lm75_reg_to_mc(_temp, data->resolution);
-
+ unsigned int regval;
+ int err, reg;
+
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->sample_time;
+ break;;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ reg = LM75_REG_TEMP;
+ break;
+ case hwmon_temp_max:
+ reg = LM75_REG_MAX;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = LM75_REG_HYST;
+ break;
+ default:
+ return -EINVAL;
+ }
+ err = regmap_read(data->regmap, reg, &regval);
+ if (err < 0)
+ return err;
+
+ *val = lm75_reg_to_mc(regval, data->resolution);
+ break;
+ default:
+ return -EINVAL;
+ }
return 0;
}
-static ssize_t show_temp(struct device *dev, struct device_attribute *da,
- char *buf)
+static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long temp)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct lm75_data *data = dev_get_drvdata(dev);
- unsigned int temp = 0;
- int err;
-
- err = regmap_read(data->regmap, attr->index, &temp);
- if (err < 0)
- return err;
-
- return sprintf(buf, "%ld\n", lm75_reg_to_mc(temp, data->resolution));
-}
-
-static ssize_t set_temp(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- struct lm75_data *data = dev_get_drvdata(dev);
- long temp;
- int error;
u8 resolution;
+ int reg;
+
+ if (type != hwmon_temp)
+ return -EINVAL;
- error = kstrtol(buf, 10, &temp);
- if (error)
- return error;
+ switch (attr) {
+ case hwmon_temp_max:
+ reg = LM75_REG_MAX;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = LM75_REG_HYST;
+ break;
+ default:
+ return -EINVAL;
+ }
/*
* Resolution of limit registers is assumed to be the same as the
* temperature input register resolution unless given explicitly.
*/
- if (attr->index && data->resolution_limits)
+ if (data->resolution_limits)
resolution = data->resolution_limits;
else
resolution = data->resolution;
@@ -145,45 +163,77 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
temp = DIV_ROUND_CLOSEST(temp << (resolution - 8),
1000) << (16 - resolution);
- error = regmap_write(data->regmap, attr->index, temp);
- if (error < 0)
- return error;
- return count;
+ return regmap_write(data->regmap, reg, temp);
}
-static ssize_t show_update_interval(struct device *dev,
- struct device_attribute *da, char *buf)
+static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct lm75_data *data = dev_get_drvdata(dev);
-
- return sprintf(buf, "%u\n", data->sample_time);
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return S_IRUGO;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return S_IRUGO;
+ case hwmon_temp_max:
+ case hwmon_temp_max_hyst:
+ return S_IRUGO | S_IWUSR;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
}
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
- show_temp, set_temp, LM75_REG_MAX);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
- show_temp, set_temp, LM75_REG_HYST);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, LM75_REG_TEMP);
-static DEVICE_ATTR(update_interval, S_IRUGO, show_update_interval, NULL);
+/*-----------------------------------------------------------------------*/
-static struct attribute *lm75_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &dev_attr_update_interval.attr,
+/* device probe and removal */
- NULL
+/* chip configuration */
+
+static const u32 lm75_chip_config[] = {
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
+ 0
};
-ATTRIBUTE_GROUPS(lm75);
-static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = {
- .get_temp = lm75_read_temp,
+static const struct hwmon_channel_info lm75_chip = {
+ .type = hwmon_chip,
+ .config = lm75_chip_config,
};
-/*-----------------------------------------------------------------------*/
+static const u32 lm75_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST,
+ 0
+};
-/* device probe and removal */
+static const struct hwmon_channel_info lm75_temp = {
+ .type = hwmon_temp,
+ .config = lm75_temp_config,
+};
+
+static const struct hwmon_channel_info *lm75_info[] = {
+ &lm75_chip,
+ &lm75_temp,
+ NULL
+};
+
+static const struct hwmon_ops lm75_hwmon_ops = {
+ .is_visible = lm75_is_visible,
+ .read = lm75_read,
+ .write = lm75_write,
+};
+
+static const struct hwmon_chip_info lm75_chip_info = {
+ .ops = &lm75_hwmon_ops,
+ .info = lm75_info,
+};
static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg)
{
@@ -337,15 +387,12 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_dbg(dev, "Config %02x\n", new);
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data, lm75_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &lm75_chip_info,
+ NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
- devm_thermal_zone_of_sensor_register(hwmon_dev, 0,
- hwmon_dev,
- &lm75_of_thermal_ops);
-
dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name);
return 0;
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 496e771..322ed92 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -89,7 +89,6 @@
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
@@ -326,7 +325,7 @@ static const struct lm90_params lm90_params[] = {
.alert_alarms = 0x7c,
.max_convrate = 9,
.reg_local_ext = TMP451_REG_R_LOCAL_TEMPL,
- }
+ },
};
/*
@@ -365,7 +364,10 @@ enum lm90_temp11_reg_index {
struct lm90_data {
struct i2c_client *client;
- const struct attribute_group *groups[6];
+ u32 channel_config[4];
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[3];
+ struct hwmon_chip_info chip;
struct mutex update_lock;
bool valid; /* true if register values are valid */
unsigned long last_updated; /* in jiffies */
@@ -489,11 +491,11 @@ static inline int lm90_select_remote_channel(struct i2c_client *client,
* client->update_lock must be held when calling this function (unless we are
* in detection or initialization steps).
*/
-static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
- unsigned int interval)
+static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
+ unsigned int interval)
{
- int i;
unsigned int update_interval;
+ int i, err;
/* Shift calculations to avoid rounding errors */
interval <<= 6;
@@ -504,8 +506,9 @@ static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
if (interval >= update_interval * 3 / 4)
break;
- i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i);
+ err = i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i);
data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64);
+ return err;
}
static int lm90_update_limits(struct device *dev)
@@ -604,19 +607,17 @@ static int lm90_update_limits(struct device *dev)
return 0;
}
-static struct lm90_data *lm90_update_device(struct device *dev)
+static int lm90_update_device(struct device *dev)
{
struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long next_update;
- int val = 0;
-
- mutex_lock(&data->update_lock);
+ int val;
if (!data->valid) {
val = lm90_update_limits(dev);
if (val < 0)
- goto error;
+ return val;
}
next_update = data->last_updated +
@@ -628,53 +629,55 @@ static struct lm90_data *lm90_update_device(struct device *dev)
val = lm90_read_reg(client, LM90_REG_R_LOCAL_LOW);
if (val < 0)
- goto error;
+ return val;
data->temp8[LOCAL_LOW] = val;
val = lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH);
if (val < 0)
- goto error;
+ return val;
data->temp8[LOCAL_HIGH] = val;
if (data->reg_local_ext) {
val = lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
data->reg_local_ext);
if (val < 0)
- goto error;
+ return val;
data->temp11[LOCAL_TEMP] = val;
} else {
val = lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP);
if (val < 0)
- goto error;
+ return val;
data->temp11[LOCAL_TEMP] = val << 8;
}
val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL);
if (val < 0)
- goto error;
+ return val;
data->temp11[REMOTE_TEMP] = val;
val = lm90_read_reg(client, LM90_REG_R_STATUS);
if (val < 0)
- goto error;
+ return val;
data->alarms = val; /* lower 8 bit of alarms */
if (data->kind == max6696) {
val = lm90_select_remote_channel(client, data, 1);
if (val < 0)
- goto error;
+ return val;
val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL);
- if (val < 0)
- goto error;
+ if (val < 0) {
+ lm90_select_remote_channel(client, data, 0);
+ return val;
+ }
data->temp11[REMOTE2_TEMP] = val;
lm90_select_remote_channel(client, data, 0);
val = lm90_read_reg(client, MAX6696_REG_R_STATUS2);
if (val < 0)
- goto error;
+ return val;
data->alarms |= val << 8;
}
@@ -686,7 +689,7 @@ static struct lm90_data *lm90_update_device(struct device *dev)
!(data->alarms & data->alert_alarms)) {
val = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (val < 0)
- goto error;
+ return val;
if (val & 0x80) {
dev_dbg(&client->dev, "Re-enabling ALERT#\n");
@@ -700,13 +703,7 @@ static struct lm90_data *lm90_update_device(struct device *dev)
data->valid = true;
}
-error:
- mutex_unlock(&data->update_lock);
-
- if (val < 0)
- return ERR_PTR(val);
-
- return data;
+ return 0;
}
/*
@@ -832,52 +829,19 @@ static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
return (val + 125) / 250 * 64;
}
-/*
- * Sysfs stuff
- */
-
-static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
- char *buf)
+/* pec used for ADM1032 only */
+static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
+ char *buf)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct lm90_data *data = lm90_update_device(dev);
- int temp;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- if (data->kind == adt7461 || data->kind == tmp451)
- temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
- else if (data->kind == max6646)
- temp = temp_from_u8(data->temp8[attr->index]);
- else
- temp = temp_from_s8(data->temp8[attr->index]);
-
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && attr->index == 3)
- temp += 16000;
+ struct i2c_client *client = to_i2c_client(dev);
- return sprintf(buf, "%d\n", temp);
+ return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
}
-static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t set_pec(struct device *dev, struct device_attribute *dummy,
+ const char *buf, size_t count)
{
- static const u8 reg[TEMP8_REG_NUM] = {
- LM90_REG_W_LOCAL_LOW,
- LM90_REG_W_LOCAL_HIGH,
- LM90_REG_W_LOCAL_CRIT,
- LM90_REG_W_REMOTE_CRIT,
- MAX6659_REG_W_LOCAL_EMERG,
- MAX6659_REG_W_REMOTE_EMERG,
- LM90_REG_W_REMOTE_CRIT,
- MAX6659_REG_W_REMOTE_EMERG,
- };
-
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct lm90_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- int nr = attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
long val;
int err;
@@ -885,82 +849,61 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
if (err < 0)
return err;
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && attr->index == 3)
- val -= 16000;
-
- mutex_lock(&data->update_lock);
- if (data->kind == adt7461 || data->kind == tmp451)
- data->temp8[nr] = temp_to_u8_adt7461(data, val);
- else if (data->kind == max6646)
- data->temp8[nr] = temp_to_u8(val);
- else
- data->temp8[nr] = temp_to_s8(val);
-
- lm90_select_remote_channel(client, data, nr >= 6);
- i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]);
- lm90_select_remote_channel(client, data, 0);
+ switch (val) {
+ case 0:
+ client->flags &= ~I2C_CLIENT_PEC;
+ break;
+ case 1:
+ client->flags |= I2C_CLIENT_PEC;
+ break;
+ default:
+ return -EINVAL;
+ }
- mutex_unlock(&data->update_lock);
return count;
}
-static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
- char *buf)
+static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
+
+static int lm90_get_temp11(struct lm90_data *data, int index)
{
- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
- struct lm90_data *data = lm90_update_device(dev);
+ s16 temp11 = data->temp11[index];
int temp;
- if (IS_ERR(data))
- return PTR_ERR(data);
-
if (data->kind == adt7461 || data->kind == tmp451)
- temp = temp_from_u16_adt7461(data, data->temp11[attr->index]);
+ temp = temp_from_u16_adt7461(data, temp11);
else if (data->kind == max6646)
- temp = temp_from_u16(data->temp11[attr->index]);
+ temp = temp_from_u16(temp11);
else
- temp = temp_from_s16(data->temp11[attr->index]);
+ temp = temp_from_s16(temp11);
/* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && attr->index <= 2)
+ if (data->kind == lm99 && index <= 2)
temp += 16000;
- return sprintf(buf, "%d\n", temp);
+ return temp;
}
-static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static int lm90_set_temp11(struct lm90_data *data, int index, long val)
{
- struct {
+ static struct reg {
u8 high;
u8 low;
- int channel;
- } reg[5] = {
- { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 0 },
- { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 0 },
- { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL, 0 },
- { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 1 },
- { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 1 }
+ } reg[] = {
+ [REMOTE_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL },
+ [REMOTE_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL },
+ [REMOTE_OFFSET] = { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL },
+ [REMOTE2_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL },
+ [REMOTE2_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }
};
-
- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
- struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- int nr = attr->nr;
- int index = attr->index;
- long val;
+ struct reg *regp = &reg[index];
int err;
- err = kstrtol(buf, 10, &val);
- if (err < 0)
- return err;
-
/* +16 degrees offset for temp2 for the LM99 */
if (data->kind == lm99 && index <= 2)
val -= 16000;
- mutex_lock(&data->update_lock);
if (data->kind == adt7461 || data->kind == tmp451)
data->temp11[index] = temp_to_u16_adt7461(data, val);
else if (data->kind == max6646)
@@ -970,317 +913,383 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
else
data->temp11[index] = temp_to_s8(val) << 8;
- lm90_select_remote_channel(client, data, reg[nr].channel);
- i2c_smbus_write_byte_data(client, reg[nr].high,
+ lm90_select_remote_channel(client, data, index >= 3);
+ err = i2c_smbus_write_byte_data(client, regp->high,
data->temp11[index] >> 8);
+ if (err < 0)
+ return err;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT)
- i2c_smbus_write_byte_data(client, reg[nr].low,
- data->temp11[index] & 0xff);
- lm90_select_remote_channel(client, data, 0);
+ err = i2c_smbus_write_byte_data(client, regp->low,
+ data->temp11[index] & 0xff);
- mutex_unlock(&data->update_lock);
- return count;
+ lm90_select_remote_channel(client, data, 0);
+ return err;
}
-static ssize_t show_temphyst(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
+static int lm90_get_temp8(struct lm90_data *data, int index)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct lm90_data *data = lm90_update_device(dev);
+ s8 temp8 = data->temp8[index];
int temp;
- if (IS_ERR(data))
- return PTR_ERR(data);
-
if (data->kind == adt7461 || data->kind == tmp451)
- temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
+ temp = temp_from_u8_adt7461(data, temp8);
else if (data->kind == max6646)
- temp = temp_from_u8(data->temp8[attr->index]);
+ temp = temp_from_u8(temp8);
else
- temp = temp_from_s8(data->temp8[attr->index]);
+ temp = temp_from_s8(temp8);
/* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && attr->index == 3)
+ if (data->kind == lm99 && index == 3)
temp += 16000;
- return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst));
+ return temp;
}
-static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
- const char *buf, size_t count)
+static int lm90_set_temp8(struct lm90_data *data, int index, long val)
{
- struct lm90_data *data = dev_get_drvdata(dev);
+ static const u8 reg[TEMP8_REG_NUM] = {
+ LM90_REG_W_LOCAL_LOW,
+ LM90_REG_W_LOCAL_HIGH,
+ LM90_REG_W_LOCAL_CRIT,
+ LM90_REG_W_REMOTE_CRIT,
+ MAX6659_REG_W_LOCAL_EMERG,
+ MAX6659_REG_W_REMOTE_EMERG,
+ LM90_REG_W_REMOTE_CRIT,
+ MAX6659_REG_W_REMOTE_EMERG,
+ };
struct i2c_client *client = data->client;
- long val;
int err;
- int temp;
- err = kstrtol(buf, 10, &val);
- if (err < 0)
- return err;
+ /* +16 degrees offset for temp2 for the LM99 */
+ if (data->kind == lm99 && index == 3)
+ val -= 16000;
- mutex_lock(&data->update_lock);
if (data->kind == adt7461 || data->kind == tmp451)
- temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]);
+ data->temp8[index] = temp_to_u8_adt7461(data, val);
else if (data->kind == max6646)
- temp = temp_from_u8(data->temp8[LOCAL_CRIT]);
+ data->temp8[index] = temp_to_u8(val);
else
- temp = temp_from_s8(data->temp8[LOCAL_CRIT]);
+ data->temp8[index] = temp_to_s8(val);
- data->temp_hyst = hyst_to_reg(temp - val);
- i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
- data->temp_hyst);
- mutex_unlock(&data->update_lock);
- return count;
-}
-
-static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
- char *buf)
-{
- struct lm90_data *data = lm90_update_device(dev);
-
- if (IS_ERR(data))
- return PTR_ERR(data);
+ lm90_select_remote_channel(client, data, index >= 6);
+ err = i2c_smbus_write_byte_data(client, reg[index], data->temp8[index]);
+ lm90_select_remote_channel(client, data, 0);
- return sprintf(buf, "%d\n", data->alarms);
+ return err;
}
-static ssize_t show_alarm(struct device *dev, struct device_attribute
- *devattr, char *buf)
+static int lm90_get_temphyst(struct lm90_data *data, int index)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct lm90_data *data = lm90_update_device(dev);
- int bitnr = attr->index;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
+ int temp;
- return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
-}
+ if (data->kind == adt7461 || data->kind == tmp451)
+ temp = temp_from_u8_adt7461(data, data->temp8[index]);
+ else if (data->kind == max6646)
+ temp = temp_from_u8(data->temp8[index]);
+ else
+ temp = temp_from_s8(data->temp8[index]);
-static ssize_t show_update_interval(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct lm90_data *data = dev_get_drvdata(dev);
+ /* +16 degrees offset for temp2 for the LM99 */
+ if (data->kind == lm99 && index == 3)
+ temp += 16000;
- return sprintf(buf, "%u\n", data->update_interval);
+ return temp - temp_from_s8(data->temp_hyst);
}
-static ssize_t set_update_interval(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm90_set_temphyst(struct lm90_data *data, long val)
{
- struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- unsigned long val;
+ int temp;
int err;
- err = kstrtoul(buf, 10, &val);
- if (err)
- return err;
-
- mutex_lock(&data->update_lock);
- lm90_set_convrate(client, data, clamp_val(val, 0, 100000));
- mutex_unlock(&data->update_lock);
+ if (data->kind == adt7461 || data->kind == tmp451)
+ temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]);
+ else if (data->kind == max6646)
+ temp = temp_from_u8(data->temp8[LOCAL_CRIT]);
+ else
+ temp = temp_from_s8(data->temp8[LOCAL_CRIT]);
- return count;
+ data->temp_hyst = hyst_to_reg(temp - val);
+ err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
+ data->temp_hyst);
+ return err;
}
-static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL,
- 0, LOCAL_TEMP);
-static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL,
- 0, REMOTE_TEMP);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, LOCAL_LOW);
-static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 0, REMOTE_LOW);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, LOCAL_HIGH);
-static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 1, REMOTE_HIGH);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, LOCAL_CRIT);
-static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, REMOTE_CRIT);
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
- set_temphyst, LOCAL_CRIT);
-static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL,
- REMOTE_CRIT);
-static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 2, REMOTE_OFFSET);
-
-/* Individual alarm files */
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5);
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
-/* Raw alarm file for compatibility */
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
-
-static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
- set_update_interval);
-
-static struct attribute *lm90_attributes[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp1_min.dev_attr.attr,
- &sensor_dev_attr_temp2_min.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp2_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
-
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- &dev_attr_alarms.attr,
- &dev_attr_update_interval.attr,
- NULL
+static const u8 lm90_temp_index[3] = {
+ LOCAL_TEMP, REMOTE_TEMP, REMOTE2_TEMP
};
-static const struct attribute_group lm90_group = {
- .attrs = lm90_attributes,
+static const u8 lm90_temp_min_index[3] = {
+ LOCAL_LOW, REMOTE_LOW, REMOTE2_LOW
};
-static struct attribute *lm90_temp2_offset_attributes[] = {
- &sensor_dev_attr_temp2_offset.dev_attr.attr,
- NULL
+static const u8 lm90_temp_max_index[3] = {
+ LOCAL_HIGH, REMOTE_HIGH, REMOTE2_HIGH
};
-static const struct attribute_group lm90_temp2_offset_group = {
- .attrs = lm90_temp2_offset_attributes,
+static const u8 lm90_temp_crit_index[3] = {
+ LOCAL_CRIT, REMOTE_CRIT, REMOTE2_CRIT
};
-/*
- * Additional attributes for devices with emergency sensors
- */
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, LOCAL_EMERG);
-static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, REMOTE_EMERG);
-static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst,
- NULL, LOCAL_EMERG);
-static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst,
- NULL, REMOTE_EMERG);
-
-static struct attribute *lm90_emergency_attributes[] = {
- &sensor_dev_attr_temp1_emergency.dev_attr.attr,
- &sensor_dev_attr_temp2_emergency.dev_attr.attr,
- &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_emergency_hyst.dev_attr.attr,
- NULL
+static const u8 lm90_temp_emerg_index[3] = {
+ LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG
};
-static const struct attribute_group lm90_emergency_group = {
- .attrs = lm90_emergency_attributes,
-};
+static const u8 lm90_min_alarm_bits[3] = { 5, 3, 11 };
+static const u8 lm90_max_alarm_bits[3] = { 0, 4, 12 };
+static const u8 lm90_crit_alarm_bits[3] = { 0, 1, 9 };
+static const u8 lm90_emergency_alarm_bits[3] = { 15, 13, 14 };
+static const u8 lm90_fault_bits[3] = { 0, 2, 10 };
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 15);
-static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 13);
+static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ int err;
-static struct attribute *lm90_emergency_alarm_attributes[] = {
- &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr,
- NULL
-};
+ mutex_lock(&data->update_lock);
+ err = lm90_update_device(dev);
+ mutex_unlock(&data->update_lock);
+ if (err)
+ return err;
-static const struct attribute_group lm90_emergency_alarm_group = {
- .attrs = lm90_emergency_alarm_attributes,
-};
+ switch (attr) {
+ case hwmon_temp_input:
+ *val = lm90_get_temp11(data, lm90_temp_index[channel]);
+ break;
+ case hwmon_temp_min_alarm:
+ *val = (data->alarms >> lm90_min_alarm_bits[channel]) & 1;
+ break;
+ case hwmon_temp_max_alarm:
+ *val = (data->alarms >> lm90_max_alarm_bits[channel]) & 1;
+ break;
+ case hwmon_temp_crit_alarm:
+ *val = (data->alarms >> lm90_crit_alarm_bits[channel]) & 1;
+ break;
+ case hwmon_temp_emergency_alarm:
+ *val = (data->alarms >> lm90_emergency_alarm_bits[channel]) & 1;
+ break;
+ case hwmon_temp_fault:
+ *val = (data->alarms >> lm90_fault_bits[channel]) & 1;
+ break;
+ case hwmon_temp_min:
+ if (channel == 0)
+ *val = lm90_get_temp8(data,
+ lm90_temp_min_index[channel]);
+ else
+ *val = lm90_get_temp11(data,
+ lm90_temp_min_index[channel]);
+ break;
+ case hwmon_temp_max:
+ if (channel == 0)
+ *val = lm90_get_temp8(data,
+ lm90_temp_max_index[channel]);
+ else
+ *val = lm90_get_temp11(data,
+ lm90_temp_max_index[channel]);
+ break;
+ case hwmon_temp_crit:
+ *val = lm90_get_temp8(data, lm90_temp_crit_index[channel]);
+ break;
+ case hwmon_temp_crit_hyst:
+ *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel]);
+ break;
+ case hwmon_temp_emergency:
+ *val = lm90_get_temp8(data, lm90_temp_emerg_index[channel]);
+ break;
+ case hwmon_temp_emergency_hyst:
+ *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel]);
+ break;
+ case hwmon_temp_offset:
+ *val = lm90_get_temp11(data, REMOTE_OFFSET);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
-/*
- * Additional attributes for devices with 3 temperature sensors
- */
-static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL,
- 0, REMOTE2_TEMP);
-static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 3, REMOTE2_LOW);
-static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 4, REMOTE2_HIGH);
-static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, REMOTE2_CRIT);
-static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL,
- REMOTE2_CRIT);
-static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, REMOTE2_EMERG);
-static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst,
- NULL, REMOTE2_EMERG);
-
-static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9);
-static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 10);
-static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 11);
-static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 12);
-static SENSOR_DEVICE_ATTR(temp3_emergency_alarm, S_IRUGO, show_alarm, NULL, 14);
-
-static struct attribute *lm90_temp3_attributes[] = {
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp3_min.dev_attr.attr,
- &sensor_dev_attr_temp3_max.dev_attr.attr,
- &sensor_dev_attr_temp3_crit.dev_attr.attr,
- &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp3_emergency.dev_attr.attr,
- &sensor_dev_attr_temp3_emergency_hyst.dev_attr.attr,
-
- &sensor_dev_attr_temp3_fault.dev_attr.attr,
- &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_emergency_alarm.dev_attr.attr,
- NULL
-};
+static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ int err;
-static const struct attribute_group lm90_temp3_group = {
- .attrs = lm90_temp3_attributes,
-};
+ mutex_lock(&data->update_lock);
-/* pec used for ADM1032 only */
-static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
- char *buf)
+ err = lm90_update_device(dev);
+ if (err)
+ goto error;
+
+ switch (attr) {
+ case hwmon_temp_min:
+ if (channel == 0)
+ err = lm90_set_temp8(data,
+ lm90_temp_min_index[channel],
+ val);
+ else
+ err = lm90_set_temp11(data,
+ lm90_temp_min_index[channel],
+ val);
+ break;
+ case hwmon_temp_max:
+ if (channel == 0)
+ err = lm90_set_temp8(data,
+ lm90_temp_max_index[channel],
+ val);
+ else
+ err = lm90_set_temp11(data,
+ lm90_temp_max_index[channel],
+ val);
+ break;
+ case hwmon_temp_crit:
+ err = lm90_set_temp8(data, lm90_temp_crit_index[channel], val);
+ break;
+ case hwmon_temp_crit_hyst:
+ err = lm90_set_temphyst(data, val);
+ break;
+ case hwmon_temp_emergency:
+ err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val);
+ break;
+ case hwmon_temp_offset:
+ err = lm90_set_temp11(data, REMOTE_OFFSET, val);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+error:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static umode_t lm90_temp_is_visible(const void *data, u32 attr, int channel)
{
- struct i2c_client *client = to_i2c_client(dev);
- return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_emergency_alarm:
+ case hwmon_temp_emergency_hyst:
+ case hwmon_temp_fault:
+ return S_IRUGO;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ case hwmon_temp_emergency:
+ case hwmon_temp_offset:
+ return S_IRUGO | S_IWUSR;
+ case hwmon_temp_crit_hyst:
+ if (channel == 0)
+ return S_IRUGO | S_IWUSR;
+ return S_IRUGO;
+ default:
+ return 0;
+ }
}
-static ssize_t set_pec(struct device *dev, struct device_attribute *dummy,
- const char *buf, size_t count)
+static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val)
{
- struct i2c_client *client = to_i2c_client(dev);
- long val;
+ struct lm90_data *data = dev_get_drvdata(dev);
int err;
- err = kstrtol(buf, 10, &val);
- if (err < 0)
+ mutex_lock(&data->update_lock);
+ err = lm90_update_device(dev);
+ mutex_unlock(&data->update_lock);
+ if (err)
return err;
- switch (val) {
- case 0:
- client->flags &= ~I2C_CLIENT_PEC;
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->update_interval;
break;
- case 1:
- client->flags |= I2C_CLIENT_PEC;
+ case hwmon_chip_alarms:
+ *val = data->alarms;
break;
default:
- return -EINVAL;
+ return -EOPNOTSUPP;
}
- return count;
+ return 0;
}
-static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
+static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int err;
-/*
- * Real code
- */
+ mutex_lock(&data->update_lock);
+
+ err = lm90_update_device(dev);
+ if (err)
+ goto error;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ err = lm90_set_convrate(client, data,
+ clamp_val(val, 0, 100000));
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+error:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static umode_t lm90_chip_is_visible(const void *data, u32 attr, int channel)
+{
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return S_IRUGO | S_IWUSR;
+ case hwmon_chip_alarms:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+}
+
+static int lm90_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_read(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm90_temp_read(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm90_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_write(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm90_temp_write(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_is_visible(data, attr, channel);
+ case hwmon_temp:
+ return lm90_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm90_detect(struct i2c_client *client,
@@ -1617,15 +1626,32 @@ static void lm90_regulator_disable(void *regulator)
regulator_disable(regulator);
}
+static const u32 lm90_chip_config[] = {
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL | HWMON_C_ALARMS,
+ 0
+};
+
+static const struct hwmon_channel_info lm90_chip_info = {
+ .type = hwmon_chip,
+ .config = lm90_chip_config,
+};
+
+
+static const struct hwmon_ops lm90_ops = {
+ .is_visible = lm90_is_visible,
+ .read = lm90_read,
+ .write = lm90_write,
+};
+
static int lm90_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
- struct lm90_data *data;
+ struct hwmon_channel_info *info;
struct regulator *regulator;
struct device *hwmon_dev;
- int groups = 0;
+ struct lm90_data *data;
int err;
regulator = devm_regulator_get(dev, "vcc");
@@ -1665,6 +1691,49 @@ static int lm90_probe(struct i2c_client *client,
/* Set chip capabilities */
data->flags = lm90_params[data->kind].flags;
+
+ data->chip.ops = &lm90_ops;
+ data->chip.info = data->info;
+
+ data->info[0] = &lm90_chip_info;
+ data->info[1] = &data->temp_info;
+
+ info = &data->temp_info;
+ info->type = hwmon_temp;
+ info->config = data->channel_config;
+
+ data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM;
+ data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT;
+
+ if (data->flags & LM90_HAVE_OFFSET)
+ data->channel_config[1] |= HWMON_T_OFFSET;
+
+ if (data->flags & LM90_HAVE_EMERGENCY) {
+ data->channel_config[0] |= HWMON_T_EMERGENCY |
+ HWMON_T_EMERGENCY_HYST;
+ data->channel_config[1] |= HWMON_T_EMERGENCY |
+ HWMON_T_EMERGENCY_HYST;
+ }
+
+ if (data->flags & LM90_HAVE_EMERGENCY_ALARM) {
+ data->channel_config[0] |= HWMON_T_EMERGENCY_ALARM;
+ data->channel_config[1] |= HWMON_T_EMERGENCY_ALARM;
+ }
+
+ if (data->flags & LM90_HAVE_TEMP3) {
+ data->channel_config[2] = HWMON_T_INPUT |
+ HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST |
+ HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
+ HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM |
+ HWMON_T_FAULT;
+ }
+
data->reg_local_ext = lm90_params[data->kind].reg_local_ext;
/* Set maximum conversion rate */
@@ -1677,21 +1746,10 @@ static int lm90_probe(struct i2c_client *client,
return err;
}
- /* Register sysfs hooks */
- data->groups[groups++] = &lm90_group;
-
- if (data->flags & LM90_HAVE_OFFSET)
- data->groups[groups++] = &lm90_temp2_offset_group;
-
- if (data->flags & LM90_HAVE_EMERGENCY)
- data->groups[groups++] = &lm90_emergency_group;
-
- if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
- data->groups[groups++] = &lm90_emergency_alarm_group;
-
- if (data->flags & LM90_HAVE_TEMP3)
- data->groups[groups++] = &lm90_temp3_group;
-
+ /*
+ * The 'pec' attribute is attached to the i2c device and thus created
+ * separately.
+ */
if (client->flags & I2C_CLIENT_PEC) {
err = device_create_file(dev, &dev_attr_pec);
if (err)
@@ -1701,8 +1759,9 @@ static int lm90_probe(struct i2c_client *client,
return err;
}
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data, data->groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &data->chip,
+ NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index cdf19ad..8c573e6 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -15,22 +15,17 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
#include <linux/init.h>
-#include <linux/slab.h>
#include <linux/jiffies.h>
-#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/sysfs.h>
+#include <linux/slab.h>
#define DEVNAME "lm95241"
@@ -54,26 +49,25 @@ static const unsigned short normal_i2c[] = {
#define LM95241_REG_RW_REMOTE_MODEL 0x30
/* LM95241 specific bitfields */
-#define CFG_STOP 0x40
-#define CFG_CR0076 0x00
-#define CFG_CR0182 0x10
-#define CFG_CR1000 0x20
-#define CFG_CR2700 0x30
-#define R1MS_SHIFT 0
-#define R2MS_SHIFT 2
-#define R1MS_MASK (0x01 << (R1MS_SHIFT))
-#define R2MS_MASK (0x01 << (R2MS_SHIFT))
-#define R1DF_SHIFT 1
-#define R2DF_SHIFT 2
-#define R1DF_MASK (0x01 << (R1DF_SHIFT))
-#define R2DF_MASK (0x01 << (R2DF_SHIFT))
-#define R1FE_MASK 0x01
-#define R2FE_MASK 0x05
-#define TT1_SHIFT 0
-#define TT2_SHIFT 4
-#define TT_OFF 0
-#define TT_ON 1
-#define TT_MASK 7
+#define CFG_STOP BIT(6)
+#define CFG_CR0076 0x00
+#define CFG_CR0182 BIT(4)
+#define CFG_CR1000 BIT(5)
+#define CFG_CR2700 (BIT(4) | BIT(5))
+#define CFG_CRMASK (BIT(4) | BIT(5))
+#define R1MS_MASK BIT(0)
+#define R2MS_MASK BIT(2)
+#define R1DF_MASK BIT(1)
+#define R2DF_MASK BIT(2)
+#define R1FE_MASK BIT(0)
+#define R2FE_MASK BIT(2)
+#define R1DM BIT(0)
+#define R2DM BIT(1)
+#define TT1_SHIFT 0
+#define TT2_SHIFT 4
+#define TT_OFF 0
+#define TT_ON 1
+#define TT_MASK 7
#define NATSEMI_MAN_ID 0x01
#define LM95231_CHIP_ID 0xA1
#define LM95241_CHIP_ID 0xA4
@@ -91,11 +85,12 @@ static const u8 lm95241_reg_address[] = {
struct lm95241_data {
struct i2c_client *client;
struct mutex update_lock;
- unsigned long last_updated, interval; /* in jiffies */
+ unsigned long last_updated; /* in jiffies */
+ unsigned long interval; /* in milli-seconds */
char valid; /* zero until following fields are valid */
/* registers values */
u8 temp[ARRAY_SIZE(lm95241_reg_address)];
- u8 config, model, trutherm;
+ u8 status, config, model, trutherm;
};
/* Conversions */
@@ -118,7 +113,8 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
mutex_lock(&data->update_lock);
- if (time_after(jiffies, data->last_updated + data->interval) ||
+ if (time_after(jiffies, data->last_updated
+ + msecs_to_jiffies(data->interval)) ||
!data->valid) {
int i;
@@ -127,6 +123,9 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
data->temp[i]
= i2c_smbus_read_byte_data(client,
lm95241_reg_address[i]);
+
+ data->status = i2c_smbus_read_byte_data(client,
+ LM95241_REG_R_STATUS);
data->last_updated = jiffies;
data->valid = 1;
}
@@ -136,197 +135,241 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
return data;
}
-/* Sysfs stuff */
-static ssize_t show_input(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct lm95241_data *data = lm95241_update_device(dev);
- int index = to_sensor_dev_attr(attr)->index;
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n",
- index == 0 || (data->config & (1 << (index / 2))) ?
- temp_from_reg_signed(data->temp[index], data->temp[index + 1]) :
- temp_from_reg_unsigned(data->temp[index],
- data->temp[index + 1]));
-}
-
-static ssize_t show_type(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95241_read_chip(struct device *dev, u32 attr, int channel,
+ long *val)
{
struct lm95241_data *data = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE - 1,
- data->model & to_sensor_dev_attr(attr)->index ? "1\n" : "2\n");
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->interval;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_type(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95241_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
{
- struct lm95241_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- unsigned long val;
- int shift;
- u8 mask = to_sensor_dev_attr(attr)->index;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
- if (val != 1 && val != 2)
- return -EINVAL;
-
- shift = mask == R1MS_MASK ? TT1_SHIFT : TT2_SHIFT;
-
- mutex_lock(&data->update_lock);
+ struct lm95241_data *data = lm95241_update_device(dev);
- data->trutherm &= ~(TT_MASK << shift);
- if (val == 1) {
- data->model |= mask;
- data->trutherm |= (TT_ON << shift);
- } else {
- data->model &= ~mask;
- data->trutherm |= (TT_OFF << shift);
+ switch (attr) {
+ case hwmon_temp_input:
+ if (!channel || (data->config & BIT(channel - 1)))
+ *val = temp_from_reg_signed(data->temp[channel * 2],
+ data->temp[channel * 2 + 1]);
+ else
+ *val = temp_from_reg_unsigned(data->temp[channel * 2],
+ data->temp[channel * 2 + 1]);
+ return 0;
+ case hwmon_temp_min:
+ if (channel == 1)
+ *val = (data->config & R1DF_MASK) ? -128000 : 0;
+ else
+ *val = (data->config & R2DF_MASK) ? -128000 : 0;
+ return 0;
+ case hwmon_temp_max:
+ if (channel == 1)
+ *val = (data->config & R1DF_MASK) ? 127875 : 255875;
+ else
+ *val = (data->config & R2DF_MASK) ? 127875 : 255875;
+ return 0;
+ case hwmon_temp_type:
+ if (channel == 1)
+ *val = (data->model & R1MS_MASK) ? 1 : 2;
+ else
+ *val = (data->model & R2MS_MASK) ? 1 : 2;
+ return 0;
+ case hwmon_temp_fault:
+ if (channel == 1)
+ *val = !!(data->status & R1DM);
+ else
+ *val = !!(data->status & R2DM);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
}
- data->valid = 0;
-
- i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL,
- data->model);
- i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM,
- data->trutherm);
-
- mutex_unlock(&data->update_lock);
-
- return count;
}
-static ssize_t show_min(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95241_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct lm95241_data *data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE - 1,
- data->config & to_sensor_dev_attr(attr)->index ?
- "-127000\n" : "0\n");
+ switch (type) {
+ case hwmon_chip:
+ return lm95241_read_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95241_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_min(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95241_write_chip(struct device *dev, u32 attr, int channel,
+ long val)
{
struct lm95241_data *data = dev_get_drvdata(dev);
- long val;
-
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
- if (val < -128000)
- return -EINVAL;
+ int convrate;
+ u8 config;
+ int ret;
mutex_lock(&data->update_lock);
- if (val < 0)
- data->config |= to_sensor_dev_attr(attr)->index;
- else
- data->config &= ~to_sensor_dev_attr(attr)->index;
- data->valid = 0;
-
- i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG,
- data->config);
-
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ config = data->config & ~CFG_CRMASK;
+ if (val < 130) {
+ convrate = 76;
+ config |= CFG_CR0076;
+ } else if (val < 590) {
+ convrate = 182;
+ config |= CFG_CR0182;
+ } else if (val < 1850) {
+ convrate = 1000;
+ config |= CFG_CR1000;
+ } else {
+ convrate = 2700;
+ config |= CFG_CR2700;
+ }
+ data->interval = convrate;
+ data->config = config;
+ ret = i2c_smbus_write_byte_data(data->client,
+ LM95241_REG_RW_CONFIG, config);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
mutex_unlock(&data->update_lock);
-
- return count;
+ return ret;
}
-static ssize_t show_max(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95241_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
{
struct lm95241_data *data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE - 1,
- data->config & to_sensor_dev_attr(attr)->index ?
- "127000\n" : "255000\n");
-}
-
-static ssize_t set_max(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct lm95241_data *data = dev_get_drvdata(dev);
- long val;
-
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
- if (val >= 256000)
- return -EINVAL;
+ struct i2c_client *client = data->client;
+ int ret;
mutex_lock(&data->update_lock);
- if (val <= 127000)
- data->config |= to_sensor_dev_attr(attr)->index;
- else
- data->config &= ~to_sensor_dev_attr(attr)->index;
- data->valid = 0;
-
- i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG,
- data->config);
+ switch (attr) {
+ case hwmon_temp_min:
+ if (channel == 1) {
+ if (val < 0)
+ data->config |= R1DF_MASK;
+ else
+ data->config &= ~R1DF_MASK;
+ } else {
+ if (val < 0)
+ data->config |= R2DF_MASK;
+ else
+ data->config &= ~R2DF_MASK;
+ }
+ data->valid = 0;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG,
+ data->config);
+ break;
+ case hwmon_temp_max:
+ if (channel == 1) {
+ if (val <= 127875)
+ data->config |= R1DF_MASK;
+ else
+ data->config &= ~R1DF_MASK;
+ } else {
+ if (val <= 127875)
+ data->config |= R2DF_MASK;
+ else
+ data->config &= ~R2DF_MASK;
+ }
+ data->valid = 0;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG,
+ data->config);
+ break;
+ case hwmon_temp_type:
+ if (val != 1 && val != 2) {
+ ret = -EINVAL;
+ break;
+ }
+ if (channel == 1) {
+ data->trutherm &= ~(TT_MASK << TT1_SHIFT);
+ if (val == 1) {
+ data->model |= R1MS_MASK;
+ data->trutherm |= (TT_ON << TT1_SHIFT);
+ } else {
+ data->model &= ~R1MS_MASK;
+ data->trutherm |= (TT_OFF << TT1_SHIFT);
+ }
+ } else {
+ data->trutherm &= ~(TT_MASK << TT2_SHIFT);
+ if (val == 1) {
+ data->model |= R2MS_MASK;
+ data->trutherm |= (TT_ON << TT2_SHIFT);
+ } else {
+ data->model &= ~R2MS_MASK;
+ data->trutherm |= (TT_OFF << TT2_SHIFT);
+ }
+ }
+ ret = i2c_smbus_write_byte_data(client,
+ LM95241_REG_RW_REMOTE_MODEL,
+ data->model);
+ if (ret < 0)
+ break;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM,
+ data->trutherm);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
mutex_unlock(&data->update_lock);
- return count;
+ return ret;
}
-static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95241_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct lm95241_data *data = lm95241_update_device(dev);
-
- return snprintf(buf, PAGE_SIZE - 1, "%lu\n", 1000 * data->interval
- / HZ);
+ switch (type) {
+ case hwmon_chip:
+ return lm95241_write_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95241_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static umode_t lm95241_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct lm95241_data *data = dev_get_drvdata(dev);
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
-
- data->interval = val * HZ / 1000;
-
- return count;
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return S_IRUGO | S_IWUSR;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return S_IRUGO;
+ case hwmon_temp_fault:
+ return S_IRUGO;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_type:
+ return S_IRUGO | S_IWUSR;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type,
- R1MS_MASK);
-static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type,
- R2MS_MASK);
-static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min,
- R1DF_MASK);
-static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min,
- R2DF_MASK);
-static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max,
- R1DF_MASK);
-static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max,
- R2DF_MASK);
-static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
- set_interval);
-
-static struct attribute *lm95241_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp2_type.dev_attr.attr,
- &sensor_dev_attr_temp3_type.dev_attr.attr,
- &sensor_dev_attr_temp2_min.dev_attr.attr,
- &sensor_dev_attr_temp3_min.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp3_max.dev_attr.attr,
- &dev_attr_update_interval.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(lm95241);
-
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm95241_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
@@ -362,8 +405,8 @@ static int lm95241_detect(struct i2c_client *new_client,
static void lm95241_init_client(struct i2c_client *client,
struct lm95241_data *data)
{
- data->interval = HZ; /* 1 sec default */
- data->config = CFG_CR0076;
+ data->interval = 1000;
+ data->config = CFG_CR1000;
data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT);
i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config);
@@ -375,6 +418,47 @@ static void lm95241_init_client(struct i2c_client *client,
data->model);
}
+static const u32 lm95241_chip_config[] = {
+ HWMON_C_UPDATE_INTERVAL,
+ 0
+};
+
+static const struct hwmon_channel_info lm95241_chip = {
+ .type = hwmon_chip,
+ .config = lm95241_chip_config,
+};
+
+static const u32 lm95241_temp_config[] = {
+ HWMON_T_INPUT,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_TYPE |
+ HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_TYPE |
+ HWMON_T_FAULT,
+ 0
+};
+
+static const struct hwmon_channel_info lm95241_temp = {
+ .type = hwmon_temp,
+ .config = lm95241_temp_config,
+};
+
+static const struct hwmon_channel_info *lm95241_info[] = {
+ &lm95241_chip,
+ &lm95241_temp,
+ NULL
+};
+
+static const struct hwmon_ops lm95241_hwmon_ops = {
+ .is_visible = lm95241_is_visible,
+ .read = lm95241_read,
+ .write = lm95241_write,
+};
+
+static const struct hwmon_chip_info lm95241_chip_info = {
+ .ops = &lm95241_hwmon_ops,
+ .info = lm95241_info,
+};
+
static int lm95241_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -392,9 +476,10 @@ static int lm95241_probe(struct i2c_client *client,
/* Initialize the LM95241 chip */
lm95241_init_client(client, data);
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data,
- lm95241_groups);
+ &lm95241_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
@@ -420,5 +505,5 @@ static struct i2c_driver lm95241_driver = {
module_i2c_driver(lm95241_driver);
MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>");
-MODULE_DESCRIPTION("LM95241 sensor driver");
+MODULE_DESCRIPTION("LM95231/LM95241 sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
index e7aef45..a3bfd88 100644
--- a/drivers/hwmon/lm95245.c
+++ b/drivers/hwmon/lm95245.c
@@ -15,22 +15,16 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/module.h>
+#include <linux/err.h>
#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
-#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/sysfs.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
@@ -89,6 +83,7 @@ static const unsigned short normal_i2c[] = {
#define RATE_CR1000 0x02
#define RATE_CR2500 0x03
+#define STATUS1_ROS 0x10
#define STATUS1_DIODE_FAULT 0x04
#define STATUS1_RTCRIT 0x02
#define STATUS1_LOC 0x01
@@ -112,14 +107,9 @@ static const u8 lm95245_reg_address[] = {
/* Client data (each client gets its own) */
struct lm95245_data {
- struct i2c_client *client;
+ struct regmap *regmap;
struct mutex update_lock;
- unsigned long last_updated; /* in jiffies */
- unsigned long interval; /* in msecs */
- bool valid; /* zero until following fields are valid */
- /* registers values */
- u8 regs[ARRAY_SIZE(lm95245_reg_address)];
- u8 config1, config2;
+ int interval; /* in msecs */
};
/* Conversions */
@@ -135,60 +125,36 @@ static int temp_from_reg_signed(u8 val_h, u8 val_l)
return temp_from_reg_unsigned(val_h, val_l);
}
-static struct lm95245_data *lm95245_update_device(struct device *dev)
+static int lm95245_read_conversion_rate(struct lm95245_data *data)
{
- struct lm95245_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
+ unsigned int rate;
+ int ret;
- mutex_lock(&data->update_lock);
-
- if (time_after(jiffies, data->last_updated
- + msecs_to_jiffies(data->interval)) || !data->valid) {
- int i;
-
- for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++)
- data->regs[i]
- = i2c_smbus_read_byte_data(client,
- lm95245_reg_address[i]);
- data->last_updated = jiffies;
- data->valid = 1;
- }
-
- mutex_unlock(&data->update_lock);
-
- return data;
-}
-
-static unsigned long lm95245_read_conversion_rate(struct i2c_client *client)
-{
- int rate;
- unsigned long interval;
-
- rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE);
+ ret = regmap_read(data->regmap, LM95245_REG_RW_CONVERS_RATE, &rate);
+ if (ret < 0)
+ return ret;
switch (rate) {
case RATE_CR0063:
- interval = 63;
+ data->interval = 63;
break;
case RATE_CR0364:
- interval = 364;
+ data->interval = 364;
break;
case RATE_CR1000:
- interval = 1000;
+ data->interval = 1000;
break;
case RATE_CR2500:
default:
- interval = 2500;
+ data->interval = 2500;
break;
}
-
- return interval;
+ return 0;
}
-static unsigned long lm95245_set_conversion_rate(struct i2c_client *client,
- unsigned long interval)
+static int lm95245_set_conversion_rate(struct lm95245_data *data, long interval)
{
- int rate;
+ int ret, rate;
if (interval <= 63) {
interval = 63;
@@ -204,221 +170,289 @@ static unsigned long lm95245_set_conversion_rate(struct i2c_client *client,
rate = RATE_CR2500;
}
- i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate);
+ ret = regmap_write(data->regmap, LM95245_REG_RW_CONVERS_RATE, rate);
+ if (ret < 0)
+ return ret;
- return interval;
-}
-
-/* Sysfs stuff */
-static ssize_t show_input(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct lm95245_data *data = lm95245_update_device(dev);
- int temp;
- int index = to_sensor_dev_attr(attr)->index;
-
- /*
- * Index 0 (Local temp) is always signed
- * Index 2 (Remote temp) has both signed and unsigned data
- * use signed calculation for remote if signed bit is set
- */
- if (index == 0 || data->regs[index] & 0x80)
- temp = temp_from_reg_signed(data->regs[index],
- data->regs[index + 1]);
- else
- temp = temp_from_reg_unsigned(data->regs[index + 2],
- data->regs[index + 3]);
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp);
-}
-
-static ssize_t show_limit(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct lm95245_data *data = lm95245_update_device(dev);
- int index = to_sensor_dev_attr(attr)->index;
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n",
- data->regs[index] * 1000);
+ data->interval = interval;
+ return 0;
}
-static ssize_t set_limit(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95245_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
{
struct lm95245_data *data = dev_get_drvdata(dev);
- int index = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = data->client;
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
-
- val /= 1000;
-
- val = clamp_val(val, 0, (index == 6 ? 127 : 255));
-
- mutex_lock(&data->update_lock);
-
- data->valid = 0;
-
- i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val);
-
- mutex_unlock(&data->update_lock);
-
- return count;
+ struct regmap *regmap = data->regmap;
+ int ret, regl, regh, regvall, regvalh;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S :
+ LM95245_REG_R_LOCAL_TEMPL_S;
+ regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S :
+ LM95245_REG_R_LOCAL_TEMPH_S;
+ ret = regmap_read(regmap, regl, &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ /*
+ * Local temp is always signed.
+ * Remote temp has both signed and unsigned data.
+ * Use signed calculation for remote if signed bit is set
+ * or if reported temperature is below signed limit.
+ */
+ if (!channel || (regvalh & 0x80) || regvalh < 0x7f) {
+ *val = temp_from_reg_signed(regvalh, regvall);
+ return 0;
+ }
+ ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = temp_from_reg_unsigned(regvalh, regvall);
+ return 0;
+ case hwmon_temp_max:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = regvalh * 1000;
+ return 0;
+ case hwmon_temp_crit:
+ regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = regvalh * 1000;
+ return 0;
+ case hwmon_temp_max_hyst:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh - regvall) * 1000;
+ return 0;
+ case hwmon_temp_crit_hyst:
+ regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh - regvall) * 1000;
+ return 0;
+ case hwmon_temp_type:
+ ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2;
+ return 0;
+ case hwmon_temp_offset:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = temp_from_reg_signed(regvalh, regvall);
+ return 0;
+ case hwmon_temp_max_alarm:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & STATUS1_ROS);
+ return 0;
+ case hwmon_temp_crit_alarm:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC));
+ return 0;
+ case hwmon_temp_fault:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & STATUS1_DIODE_FAULT);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_crit_hyst(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95245_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
{
- struct lm95245_data *data = lm95245_update_device(dev);
- int index = to_sensor_dev_attr(attr)->index;
- int hyst = data->regs[index] - data->regs[8];
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n", hyst * 1000);
+ struct lm95245_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int ret, reg;
+
+ switch (attr) {
+ case hwmon_temp_max:
+ val = clamp_val(val / 1000, 0, 255);
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, val);
+ return ret;
+ case hwmon_temp_crit:
+ reg = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ val = clamp_val(val / 1000, 0, channel ? 255 : 127);
+ ret = regmap_write(regmap, reg, val);
+ return ret;
+ case hwmon_temp_crit_hyst:
+ mutex_lock(&data->update_lock);
+ ret = regmap_read(regmap, LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT,
+ &regval);
+ if (ret < 0) {
+ mutex_unlock(&data->update_lock);
+ return ret;
+ }
+ /* Clamp to reasonable range to prevent overflow */
+ val = clamp_val(val, -1000000, 1000000);
+ val = regval - val / 1000;
+ val = clamp_val(val, 0, 31);
+ ret = regmap_write(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ val);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ case hwmon_temp_offset:
+ val = clamp_val(val, -128000, 127875);
+ val = val * 256 / 1000;
+ mutex_lock(&data->update_lock);
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL,
+ val & 0xe0);
+ if (ret < 0) {
+ mutex_unlock(&data->update_lock);
+ return ret;
+ }
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH,
+ (val >> 8) & 0xff);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ case hwmon_temp_type:
+ if (val != 1 && val != 2)
+ return -EINVAL;
+ ret = regmap_update_bits(regmap, LM95245_REG_RW_CONFIG2,
+ CFG2_REMOTE_TT,
+ val == 1 ? CFG2_REMOTE_TT : 0);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95245_read_chip(struct device *dev, u32 attr, int channel,
+ long *val)
{
struct lm95245_data *data = dev_get_drvdata(dev);
- int index = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = data->client;
- unsigned long val;
- int hyst, limit;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
-
- limit = i2c_smbus_read_byte_data(client, lm95245_reg_address[index]);
- hyst = limit - val / 1000;
- hyst = clamp_val(hyst, 0, 31);
- data->regs[8] = hyst;
- /* shared crit hysteresis */
- i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS,
- hyst);
-
- mutex_unlock(&data->update_lock);
-
- return count;
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->interval;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_type(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95245_write_chip(struct device *dev, u32 attr, int channel,
+ long val)
{
struct lm95245_data *data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE - 1,
- data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n");
+ int ret;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ mutex_lock(&data->update_lock);
+ ret = lm95245_set_conversion_rate(data, val);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_type(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95245_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct lm95245_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
- if (val != 1 && val != 2)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
-
- if (val == 1)
- data->config2 |= CFG2_REMOTE_TT;
- else
- data->config2 &= ~CFG2_REMOTE_TT;
-
- data->valid = 0;
-
- i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2,
- data->config2);
-
- mutex_unlock(&data->update_lock);
-
- return count;
+ switch (type) {
+ case hwmon_chip:
+ return lm95245_read_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95245_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95245_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct lm95245_data *data = lm95245_update_device(dev);
- int index = to_sensor_dev_attr(attr)->index;
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n",
- !!(data->regs[9] & index));
+ switch (type) {
+ case hwmon_chip:
+ return lm95245_write_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95245_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
- char *buf)
+static umode_t lm95245_temp_is_visible(const void *data, u32 attr, int channel)
{
- struct lm95245_data *data = lm95245_update_device(dev);
-
- return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval);
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_fault:
+ return S_IRUGO;
+ case hwmon_temp_type:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ case hwmon_temp_offset:
+ return S_IRUGO | S_IWUSR;
+ case hwmon_temp_crit_hyst:
+ return (channel == 0) ? S_IRUGO | S_IWUSR : S_IRUGO;
+ default:
+ return 0;
+ }
}
-static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static umode_t lm95245_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct lm95245_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
-
- data->interval = lm95245_set_conversion_rate(client, val);
-
- mutex_unlock(&data->update_lock);
-
- return count;
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return S_IRUGO | S_IWUSR;
+ default:
+ return 0;
+ }
+ case hwmon_temp:
+ return lm95245_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit,
- set_limit, 6);
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_crit_hyst,
- set_crit_hyst, 6);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
- STATUS1_LOC);
-
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit,
- set_limit, 7);
-static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_crit_hyst, NULL, 7);
-static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
- STATUS1_RTCRIT);
-static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type,
- set_type, 0);
-static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL,
- STATUS1_DIODE_FAULT);
-
-static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
- set_interval);
-
-static struct attribute *lm95245_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_crit.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_type.dev_attr.attr,
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- &dev_attr_update_interval.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(lm95245);
-
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm95245_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
@@ -453,44 +487,130 @@ static int lm95245_detect(struct i2c_client *new_client,
return 0;
}
-static void lm95245_init_client(struct i2c_client *client,
- struct lm95245_data *data)
+static int lm95245_init_client(struct lm95245_data *data)
{
- data->interval = lm95245_read_conversion_rate(client);
-
- data->config1 = i2c_smbus_read_byte_data(client,
- LM95245_REG_RW_CONFIG1);
- data->config2 = i2c_smbus_read_byte_data(client,
- LM95245_REG_RW_CONFIG2);
-
- if (data->config1 & CFG_STOP) {
- /* Clear the standby bit */
- data->config1 &= ~CFG_STOP;
- i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1,
- data->config1);
+ int ret;
+
+ ret = lm95245_read_conversion_rate(data);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(data->regmap, LM95245_REG_RW_CONFIG1,
+ CFG_STOP, 0);
+}
+
+static bool lm95245_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LM95245_REG_RW_CONFIG1:
+ case LM95245_REG_RW_CONVERS_RATE:
+ case LM95245_REG_W_ONE_SHOT:
+ case LM95245_REG_RW_CONFIG2:
+ case LM95245_REG_RW_REMOTE_OFFH:
+ case LM95245_REG_RW_REMOTE_OFFL:
+ case LM95245_REG_RW_REMOTE_OS_LIMIT:
+ case LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT:
+ case LM95245_REG_RW_REMOTE_TCRIT_LIMIT:
+ case LM95245_REG_RW_COMMON_HYSTERESIS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool lm95245_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LM95245_REG_R_STATUS1:
+ case LM95245_REG_R_STATUS2:
+ case LM95245_REG_R_LOCAL_TEMPH_S:
+ case LM95245_REG_R_LOCAL_TEMPL_S:
+ case LM95245_REG_R_REMOTE_TEMPH_S:
+ case LM95245_REG_R_REMOTE_TEMPL_S:
+ case LM95245_REG_R_REMOTE_TEMPH_U:
+ case LM95245_REG_R_REMOTE_TEMPL_U:
+ return true;
+ default:
+ return false;
}
}
+static const struct regmap_config lm95245_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = lm95245_is_writeable_reg,
+ .volatile_reg = lm95245_is_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_rw = true,
+};
+
+static const u32 lm95245_chip_config[] = {
+ HWMON_C_UPDATE_INTERVAL,
+ 0
+};
+
+static const struct hwmon_channel_info lm95245_chip = {
+ .type = hwmon_chip,
+ .config = lm95245_chip_config,
+};
+
+static const u32 lm95245_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_CRIT_ALARM,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST | HWMON_T_FAULT | HWMON_T_MAX_ALARM |
+ HWMON_T_CRIT_ALARM | HWMON_T_TYPE | HWMON_T_OFFSET,
+ 0
+};
+
+static const struct hwmon_channel_info lm95245_temp = {
+ .type = hwmon_temp,
+ .config = lm95245_temp_config,
+};
+
+static const struct hwmon_channel_info *lm95245_info[] = {
+ &lm95245_chip,
+ &lm95245_temp,
+ NULL
+};
+
+static const struct hwmon_ops lm95245_hwmon_ops = {
+ .is_visible = lm95245_is_visible,
+ .read = lm95245_read,
+ .write = lm95245_write,
+};
+
+static const struct hwmon_chip_info lm95245_chip_info = {
+ .ops = &lm95245_hwmon_ops,
+ .info = lm95245_info,
+};
+
static int lm95245_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct lm95245_data *data;
struct device *hwmon_dev;
+ int ret;
data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- data->client = client;
+ data->regmap = devm_regmap_init_i2c(client, &lm95245_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
mutex_init(&data->update_lock);
/* Initialize the LM95245 chip */
- lm95245_init_client(client, data);
-
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data,
- lm95245_groups);
+ ret = lm95245_init_client(data);
+ if (ret < 0)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &lm95245_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c
index c86a184..8445c9f 100644
--- a/drivers/hwmon/ltc4151.c
+++ b/drivers/hwmon/ltc4151.c
@@ -30,6 +30,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
@@ -52,6 +53,7 @@ struct ltc4151_data {
struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
+ unsigned int shunt; /* in micro ohms */
/* Registers */
u8 regs[6];
@@ -111,9 +113,9 @@ static int ltc4151_get_value(struct ltc4151_data *data, u8 reg)
case LTC4151_SENSE_H:
/*
* 20uV resolution. Convert to current as measured with
- * an 1 mOhm sense resistor, in mA.
+ * a given sense resistor, in mA.
*/
- val = val * 20;
+ val = val * 20 * 1000 / data->shunt;
break;
case LTC4151_VIN_H:
/* 25 mV per increment */
@@ -176,6 +178,7 @@ static int ltc4151_probe(struct i2c_client *client,
struct device *dev = &client->dev;
struct ltc4151_data *data;
struct device *hwmon_dev;
+ u32 shunt;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -184,6 +187,15 @@ static int ltc4151_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;
+ if (of_property_read_u32(client->dev.of_node,
+ "shunt-resistor-micro-ohms", &shunt))
+ shunt = 1000; /* 1 mOhm if not set via DT */
+
+ if (shunt == 0)
+ return -EINVAL;
+
+ data->shunt = shunt;
+
data->client = client;
mutex_init(&data->update_lock);
@@ -199,10 +211,16 @@ static const struct i2c_device_id ltc4151_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ltc4151_id);
+static const struct of_device_id ltc4151_match[] = {
+ { .compatible = "lltc,ltc4151" },
+ {},
+};
+
/* This is the driver that will be inserted */
static struct i2c_driver ltc4151_driver = {
.driver = {
.name = "ltc4151",
+ .of_match_table = of_match_ptr(ltc4151_match),
},
.probe = ltc4151_probe,
.id_table = ltc4151_id,
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index 681b5b7..4680d89 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
@@ -53,8 +54,6 @@ enum ltc4245_cmd {
struct ltc4245_data {
struct i2c_client *client;
- const struct attribute_group *groups[3];
-
struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
@@ -162,7 +161,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
ltc4245_update_gpios(dev);
data->last_updated = jiffies;
- data->valid = 1;
+ data->valid = true;
}
mutex_unlock(&data->update_lock);
@@ -256,213 +255,204 @@ static unsigned int ltc4245_get_current(struct device *dev, u8 reg)
return curr;
}
-static ssize_t ltc4245_show_voltage(struct device *dev,
- struct device_attribute *da,
- char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- const int voltage = ltc4245_get_voltage(dev, attr->index);
+/* Map from voltage channel index to voltage register */
- return snprintf(buf, PAGE_SIZE, "%d\n", voltage);
-}
+static const s8 ltc4245_in_regs[] = {
+ LTC4245_12VIN, LTC4245_5VIN, LTC4245_3VIN, LTC4245_VEEIN,
+ LTC4245_12VOUT, LTC4245_5VOUT, LTC4245_3VOUT, LTC4245_VEEOUT,
+};
+
+/* Map from current channel index to current register */
-static ssize_t ltc4245_show_current(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static const s8 ltc4245_curr_regs[] = {
+ LTC4245_12VSENSE, LTC4245_5VSENSE, LTC4245_3VSENSE, LTC4245_VEESENSE,
+};
+
+static int ltc4245_read_curr(struct device *dev, u32 attr, int channel,
+ long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- const unsigned int curr = ltc4245_get_current(dev, attr->index);
+ struct ltc4245_data *data = ltc4245_update_device(dev);
- return snprintf(buf, PAGE_SIZE, "%u\n", curr);
+ switch (attr) {
+ case hwmon_curr_input:
+ *val = ltc4245_get_current(dev, ltc4245_curr_regs[channel]);
+ return 0;
+ case hwmon_curr_max_alarm:
+ *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel + 4));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t ltc4245_show_power(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static int ltc4245_read_in(struct device *dev, u32 attr, int channel, long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- const unsigned int curr = ltc4245_get_current(dev, attr->index);
- const int output_voltage = ltc4245_get_voltage(dev, attr->index+1);
+ struct ltc4245_data *data = ltc4245_update_device(dev);
- /* current in mA * voltage in mV == power in uW */
- const unsigned int power = abs(output_voltage * curr);
+ switch (attr) {
+ case hwmon_in_input:
+ if (channel < 8) {
+ *val = ltc4245_get_voltage(dev,
+ ltc4245_in_regs[channel]);
+ } else {
+ int regval = data->gpios[channel - 8];
+
+ if (regval < 0)
+ return regval;
+ *val = regval * 10;
+ }
+ return 0;
+ case hwmon_in_min_alarm:
+ if (channel < 4)
+ *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel));
+ else
+ *val = !!(data->cregs[LTC4245_FAULT2] &
+ BIT(channel - 4));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
- return snprintf(buf, PAGE_SIZE, "%u\n", power);
+static int ltc4245_read_power(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ unsigned long curr;
+ long voltage;
+
+ switch (attr) {
+ case hwmon_power_input:
+ (void)ltc4245_update_device(dev);
+ curr = ltc4245_get_current(dev, ltc4245_curr_regs[channel]);
+ voltage = ltc4245_get_voltage(dev, ltc4245_in_regs[channel]);
+ *val = abs(curr * voltage);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t ltc4245_show_alarm(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static int ltc4245_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
- struct ltc4245_data *data = ltc4245_update_device(dev);
- const u8 reg = data->cregs[attr->index];
- const u32 mask = attr->nr;
- return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
+ switch (type) {
+ case hwmon_curr:
+ return ltc4245_read_curr(dev, attr, channel, val);
+ case hwmon_power:
+ return ltc4245_read_power(dev, attr, channel, val);
+ case hwmon_in:
+ return ltc4245_read_in(dev, attr, channel - 1, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t ltc4245_show_gpio(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static umode_t ltc4245_is_visible(const void *_data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- struct ltc4245_data *data = ltc4245_update_device(dev);
- int val = data->gpios[attr->index];
+ const struct ltc4245_data *data = _data;
+
+ switch (type) {
+ case hwmon_in:
+ if (channel == 0)
+ return 0;
+ switch (attr) {
+ case hwmon_in_input:
+ if (channel > 9 && !data->use_extra_gpios)
+ return 0;
+ return S_IRUGO;
+ case hwmon_in_min_alarm:
+ if (channel > 8)
+ return 0;
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_max_alarm:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
- /* handle stale GPIO's */
- if (val < 0)
- return val;
+static const u32 ltc4245_in_config[] = {
+ HWMON_I_INPUT, /* dummy, skipped in is_visible */
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ 0
+};
- /* Convert to millivolts and print */
- return snprintf(buf, PAGE_SIZE, "%u\n", val * 10);
-}
+static const struct hwmon_channel_info ltc4245_in = {
+ .type = hwmon_in,
+ .config = ltc4245_in_config,
+};
-/* Construct a sensor_device_attribute structure for each register */
-
-/* Input voltages */
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_12VIN);
-static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_5VIN);
-static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_3VIN);
-static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_VEEIN);
-
-/* Input undervoltage alarms */
-static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 0, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 1, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 2, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 3, LTC4245_FAULT1);
-
-/* Currents (via sense resistor) */
-static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL,
- LTC4245_12VSENSE);
-static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL,
- LTC4245_5VSENSE);
-static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL,
- LTC4245_3VSENSE);
-static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL,
- LTC4245_VEESENSE);
-
-/* Overcurrent alarms */
-static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 4, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 5, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 6, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 7, LTC4245_FAULT1);
-
-/* Output voltages */
-static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_12VOUT);
-static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_5VOUT);
-static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_3VOUT);
-static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_VEEOUT);
-
-/* Power Bad alarms */
-static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 0, LTC4245_FAULT2);
-static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 1, LTC4245_FAULT2);
-static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 2, LTC4245_FAULT2);
-static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 3, LTC4245_FAULT2);
-
-/* GPIO voltages */
-static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0);
-static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1);
-static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2);
-
-/* Power Consumption (virtual) */
-static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL,
- LTC4245_12VSENSE);
-static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL,
- LTC4245_5VSENSE);
-static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL,
- LTC4245_3VSENSE);
-static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL,
- LTC4245_VEESENSE);
+static const u32 ltc4245_curr_config[] = {
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ 0
+};
-/*
- * Finally, construct an array of pointers to members of the above objects,
- * as required for sysfs_create_group()
- */
-static struct attribute *ltc4245_std_attributes[] = {
- &sensor_dev_attr_in1_input.dev_attr.attr,
- &sensor_dev_attr_in2_input.dev_attr.attr,
- &sensor_dev_attr_in3_input.dev_attr.attr,
- &sensor_dev_attr_in4_input.dev_attr.attr,
-
- &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in2_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in3_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in4_min_alarm.dev_attr.attr,
-
- &sensor_dev_attr_curr1_input.dev_attr.attr,
- &sensor_dev_attr_curr2_input.dev_attr.attr,
- &sensor_dev_attr_curr3_input.dev_attr.attr,
- &sensor_dev_attr_curr4_input.dev_attr.attr,
-
- &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
- &sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
- &sensor_dev_attr_curr3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_curr4_max_alarm.dev_attr.attr,
-
- &sensor_dev_attr_in5_input.dev_attr.attr,
- &sensor_dev_attr_in6_input.dev_attr.attr,
- &sensor_dev_attr_in7_input.dev_attr.attr,
- &sensor_dev_attr_in8_input.dev_attr.attr,
-
- &sensor_dev_attr_in5_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in6_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in7_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in8_min_alarm.dev_attr.attr,
-
- &sensor_dev_attr_in9_input.dev_attr.attr,
-
- &sensor_dev_attr_power1_input.dev_attr.attr,
- &sensor_dev_attr_power2_input.dev_attr.attr,
- &sensor_dev_attr_power3_input.dev_attr.attr,
- &sensor_dev_attr_power4_input.dev_attr.attr,
-
- NULL,
+static const struct hwmon_channel_info ltc4245_curr = {
+ .type = hwmon_curr,
+ .config = ltc4245_curr_config,
};
-static struct attribute *ltc4245_gpio_attributes[] = {
- &sensor_dev_attr_in10_input.dev_attr.attr,
- &sensor_dev_attr_in11_input.dev_attr.attr,
- NULL,
+static const u32 ltc4245_power_config[] = {
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ 0
};
-static const struct attribute_group ltc4245_std_group = {
- .attrs = ltc4245_std_attributes,
+static const struct hwmon_channel_info ltc4245_power = {
+ .type = hwmon_power,
+ .config = ltc4245_power_config,
};
-static const struct attribute_group ltc4245_gpio_group = {
- .attrs = ltc4245_gpio_attributes,
+static const struct hwmon_channel_info *ltc4245_info[] = {
+ &ltc4245_in,
+ &ltc4245_curr,
+ &ltc4245_power,
+ NULL
};
-static void ltc4245_sysfs_add_groups(struct ltc4245_data *data)
-{
- /* standard sysfs attributes */
- data->groups[0] = &ltc4245_std_group;
+static const struct hwmon_ops ltc4245_hwmon_ops = {
+ .is_visible = ltc4245_is_visible,
+ .read = ltc4245_read,
+};
- /* if we're using the extra gpio support, register it's attributes */
- if (data->use_extra_gpios)
- data->groups[1] = &ltc4245_gpio_group;
-}
+static const struct hwmon_chip_info ltc4245_chip_info = {
+ .ops = &ltc4245_hwmon_ops,
+ .info = ltc4245_info,
+};
static bool ltc4245_use_extra_gpios(struct i2c_client *client)
{
@@ -502,12 +492,10 @@ static int ltc4245_probe(struct i2c_client *client,
i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00);
i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00);
- /* Add sysfs hooks */
- ltc4245_sysfs_add_groups(data);
-
- hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
- client->name, data,
- data->groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
+ client->name, data,
+ &ltc4245_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
index 69c0ac8..bef84e0 100644
--- a/drivers/hwmon/max31790.c
+++ b/drivers/hwmon/max31790.c
@@ -17,7 +17,6 @@
#include <linux/err.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
@@ -169,362 +168,288 @@ static u8 bits_for_tach_period(int rpm)
return bits;
}
-static ssize_t get_fan(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int max31790_read_fan(struct device *dev, u32 attr, int channel,
+ long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev);
int sr, rpm;
if (IS_ERR(data))
return PTR_ERR(data);
- sr = get_tach_period(data->fan_dynamics[attr->index]);
- rpm = RPM_FROM_REG(data->tach[attr->index], sr);
-
- return sprintf(buf, "%d\n", rpm);
-}
-
-static ssize_t get_fan_target(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max31790_data *data = max31790_update_device(dev);
- int sr, rpm;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- sr = get_tach_period(data->fan_dynamics[attr->index]);
- rpm = RPM_FROM_REG(data->target_count[attr->index], sr);
-
- return sprintf(buf, "%d\n", rpm);
+ switch (attr) {
+ case hwmon_fan_input:
+ sr = get_tach_period(data->fan_dynamics[channel]);
+ rpm = RPM_FROM_REG(data->tach[channel], sr);
+ *val = rpm;
+ return 0;
+ case hwmon_fan_target:
+ sr = get_tach_period(data->fan_dynamics[channel]);
+ rpm = RPM_FROM_REG(data->target_count[channel], sr);
+ *val = rpm;
+ return 0;
+ case hwmon_fan_fault:
+ *val = !!(data->fault_status & (1 << channel));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_fan_target(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static int max31790_write_fan(struct device *dev, u32 attr, int channel,
+ long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
+ int target_count;
+ int err = 0;
u8 bits;
int sr;
- int target_count;
- unsigned long rpm;
- int err;
-
- err = kstrtoul(buf, 10, &rpm);
- if (err)
- return err;
mutex_lock(&data->update_lock);
- rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
- bits = bits_for_tach_period(rpm);
- data->fan_dynamics[attr->index] =
- ((data->fan_dynamics[attr->index]
- & ~MAX31790_FAN_DYN_SR_MASK)
- | (bits << MAX31790_FAN_DYN_SR_SHIFT));
- err = i2c_smbus_write_byte_data(client,
- MAX31790_REG_FAN_DYNAMICS(attr->index),
- data->fan_dynamics[attr->index]);
-
- if (err < 0) {
- mutex_unlock(&data->update_lock);
- return err;
+ switch (attr) {
+ case hwmon_fan_target:
+ val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX);
+ bits = bits_for_tach_period(val);
+ data->fan_dynamics[channel] =
+ ((data->fan_dynamics[channel] &
+ ~MAX31790_FAN_DYN_SR_MASK) |
+ (bits << MAX31790_FAN_DYN_SR_SHIFT));
+ err = i2c_smbus_write_byte_data(client,
+ MAX31790_REG_FAN_DYNAMICS(channel),
+ data->fan_dynamics[channel]);
+ if (err < 0)
+ break;
+
+ sr = get_tach_period(data->fan_dynamics[channel]);
+ target_count = RPM_TO_REG(val, sr);
+ target_count = clamp_val(target_count, 0x1, 0x7FF);
+
+ data->target_count[channel] = target_count << 5;
+
+ err = i2c_smbus_write_word_swapped(client,
+ MAX31790_REG_TARGET_COUNT(channel),
+ data->target_count[channel]);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
}
- sr = get_tach_period(data->fan_dynamics[attr->index]);
- target_count = RPM_TO_REG(rpm, sr);
- target_count = clamp_val(target_count, 0x1, 0x7FF);
-
- data->target_count[attr->index] = target_count << 5;
-
- err = i2c_smbus_write_word_swapped(client,
- MAX31790_REG_TARGET_COUNT(attr->index),
- data->target_count[attr->index]);
-
mutex_unlock(&data->update_lock);
- if (err < 0)
- return err;
+ return err;
+}
- return count;
+static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct max31790_data *data = _data;
+ u8 fan_config = data->fan_config[channel % NR_CHANNEL];
+
+ switch (attr) {
+ case hwmon_fan_input:
+ case hwmon_fan_fault:
+ if (channel < NR_CHANNEL ||
+ (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+ return S_IRUGO;
+ return 0;
+ case hwmon_fan_target:
+ if (channel < NR_CHANNEL &&
+ !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+ return S_IRUGO | S_IWUSR;
+ return 0;
+ default:
+ return 0;
+ }
}
-static ssize_t get_pwm(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
+ long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev);
- int pwm;
+ u8 fan_config = data->fan_config[channel];
if (IS_ERR(data))
return PTR_ERR(data);
- pwm = data->pwm[attr->index] >> 8;
-
- return sprintf(buf, "%d\n", pwm);
+ switch (attr) {
+ case hwmon_pwm_input:
+ *val = data->pwm[channel] >> 8;
+ return 0;
+ case hwmon_pwm_enable:
+ if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
+ *val = 2;
+ else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN)
+ *val = 1;
+ else
+ *val = 0;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_pwm(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
+ long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- unsigned long pwm;
- int err;
-
- err = kstrtoul(buf, 10, &pwm);
- if (err)
- return err;
-
- if (pwm > 255)
- return -EINVAL;
+ u8 fan_config;
+ int err = 0;
mutex_lock(&data->update_lock);
- data->pwm[attr->index] = pwm << 8;
- err = i2c_smbus_write_word_swapped(client,
- MAX31790_REG_PWMOUT(attr->index),
- data->pwm[attr->index]);
+ switch (attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > 255) {
+ err = -EINVAL;
+ break;
+ }
+ data->pwm[channel] = val << 8;
+ err = i2c_smbus_write_word_swapped(client,
+ MAX31790_REG_PWMOUT(channel),
+ val);
+ break;
+ case hwmon_pwm_enable:
+ fan_config = data->fan_config[channel];
+ if (val == 0) {
+ fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN |
+ MAX31790_FAN_CFG_RPM_MODE);
+ } else if (val == 1) {
+ fan_config = (fan_config |
+ MAX31790_FAN_CFG_TACH_INPUT_EN) &
+ ~MAX31790_FAN_CFG_RPM_MODE;
+ } else if (val == 2) {
+ fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN |
+ MAX31790_FAN_CFG_RPM_MODE;
+ } else {
+ err = -EINVAL;
+ break;
+ }
+ data->fan_config[channel] = fan_config;
+ err = i2c_smbus_write_byte_data(client,
+ MAX31790_REG_FAN_CONFIG(channel),
+ fan_config);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
mutex_unlock(&data->update_lock);
- if (err < 0)
- return err;
-
- return count;
+ return err;
}
-static ssize_t get_pwm_enable(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static umode_t max31790_pwm_is_visible(const void *_data, u32 attr, int channel)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max31790_data *data = max31790_update_device(dev);
- int mode;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE)
- mode = 2;
- else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN)
- mode = 1;
- else
- mode = 0;
-
- return sprintf(buf, "%d\n", mode);
+ const struct max31790_data *data = _data;
+ u8 fan_config = data->fan_config[channel];
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_enable:
+ if (!(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+ return S_IRUGO | S_IWUSR;
+ return 0;
+ default:
+ return 0;
+ }
}
-static ssize_t set_pwm_enable(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static int max31790_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max31790_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- unsigned long mode;
- int err;
-
- err = kstrtoul(buf, 10, &mode);
- if (err)
- return err;
-
- switch (mode) {
- case 0:
- data->fan_config[attr->index] =
- data->fan_config[attr->index]
- & ~(MAX31790_FAN_CFG_TACH_INPUT_EN
- | MAX31790_FAN_CFG_RPM_MODE);
- break;
- case 1:
- data->fan_config[attr->index] =
- (data->fan_config[attr->index]
- | MAX31790_FAN_CFG_TACH_INPUT_EN)
- & ~MAX31790_FAN_CFG_RPM_MODE;
- break;
- case 2:
- data->fan_config[attr->index] =
- data->fan_config[attr->index]
- | MAX31790_FAN_CFG_TACH_INPUT_EN
- | MAX31790_FAN_CFG_RPM_MODE;
- break;
+ switch (type) {
+ case hwmon_fan:
+ return max31790_read_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return max31790_read_pwm(dev, attr, channel, val);
default:
- return -EINVAL;
+ return -EOPNOTSUPP;
}
-
- mutex_lock(&data->update_lock);
-
- err = i2c_smbus_write_byte_data(client,
- MAX31790_REG_FAN_CONFIG(attr->index),
- data->fan_config[attr->index]);
-
- mutex_unlock(&data->update_lock);
-
- if (err < 0)
- return err;
-
- return count;
}
-static ssize_t get_fan_fault(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int max31790_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max31790_data *data = max31790_update_device(dev);
- int fault;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- fault = !!(data->fault_status & (1 << attr->index));
+ switch (type) {
+ case hwmon_fan:
+ return max31790_write_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return max31790_write_pwm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
- return sprintf(buf, "%d\n", fault);
+static umode_t max31790_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_fan:
+ return max31790_fan_is_visible(data, attr, channel);
+ case hwmon_pwm:
+ return max31790_pwm_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
}
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
-static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4);
-static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5);
-
-static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3);
-static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4);
-static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5);
-
-static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6);
-static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7);
-static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8);
-static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9);
-static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10);
-static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11);
-
-static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6);
-static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7);
-static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8);
-static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9);
-static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10);
-static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11);
-
-static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 0);
-static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 1);
-static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 2);
-static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 3);
-static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 4);
-static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 5);
-
-static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0);
-static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1);
-static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2);
-static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3);
-static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4);
-static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5);
-
-static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 0);
-static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 1);
-static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 2);
-static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 3);
-static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 4);
-static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 5);
-
-static struct attribute *max31790_attrs[] = {
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- &sensor_dev_attr_fan2_input.dev_attr.attr,
- &sensor_dev_attr_fan3_input.dev_attr.attr,
- &sensor_dev_attr_fan4_input.dev_attr.attr,
- &sensor_dev_attr_fan5_input.dev_attr.attr,
- &sensor_dev_attr_fan6_input.dev_attr.attr,
-
- &sensor_dev_attr_fan1_fault.dev_attr.attr,
- &sensor_dev_attr_fan2_fault.dev_attr.attr,
- &sensor_dev_attr_fan3_fault.dev_attr.attr,
- &sensor_dev_attr_fan4_fault.dev_attr.attr,
- &sensor_dev_attr_fan5_fault.dev_attr.attr,
- &sensor_dev_attr_fan6_fault.dev_attr.attr,
-
- &sensor_dev_attr_fan7_input.dev_attr.attr,
- &sensor_dev_attr_fan8_input.dev_attr.attr,
- &sensor_dev_attr_fan9_input.dev_attr.attr,
- &sensor_dev_attr_fan10_input.dev_attr.attr,
- &sensor_dev_attr_fan11_input.dev_attr.attr,
- &sensor_dev_attr_fan12_input.dev_attr.attr,
-
- &sensor_dev_attr_fan7_fault.dev_attr.attr,
- &sensor_dev_attr_fan8_fault.dev_attr.attr,
- &sensor_dev_attr_fan9_fault.dev_attr.attr,
- &sensor_dev_attr_fan10_fault.dev_attr.attr,
- &sensor_dev_attr_fan11_fault.dev_attr.attr,
- &sensor_dev_attr_fan12_fault.dev_attr.attr,
-
- &sensor_dev_attr_fan1_target.dev_attr.attr,
- &sensor_dev_attr_fan2_target.dev_attr.attr,
- &sensor_dev_attr_fan3_target.dev_attr.attr,
- &sensor_dev_attr_fan4_target.dev_attr.attr,
- &sensor_dev_attr_fan5_target.dev_attr.attr,
- &sensor_dev_attr_fan6_target.dev_attr.attr,
-
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_pwm2.dev_attr.attr,
- &sensor_dev_attr_pwm3.dev_attr.attr,
- &sensor_dev_attr_pwm4.dev_attr.attr,
- &sensor_dev_attr_pwm5.dev_attr.attr,
- &sensor_dev_attr_pwm6.dev_attr.attr,
-
- &sensor_dev_attr_pwm1_enable.dev_attr.attr,
- &sensor_dev_attr_pwm2_enable.dev_attr.attr,
- &sensor_dev_attr_pwm3_enable.dev_attr.attr,
- &sensor_dev_attr_pwm4_enable.dev_attr.attr,
- &sensor_dev_attr_pwm5_enable.dev_attr.attr,
- &sensor_dev_attr_pwm6_enable.dev_attr.attr,
- NULL
+static const u32 max31790_fan_config[] = {
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ 0
};
-static umode_t max31790_attrs_visible(struct kobject *kobj,
- struct attribute *a, int n)
-{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct max31790_data *data = dev_get_drvdata(dev);
- struct device_attribute *devattr =
- container_of(a, struct device_attribute, attr);
- int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL;
- u8 fan_config;
+static const struct hwmon_channel_info max31790_fan = {
+ .type = hwmon_fan,
+ .config = max31790_fan_config,
+};
- fan_config = data->fan_config[index];
+static const u32 max31790_pwm_config[] = {
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ 0
+};
- if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 &&
- !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
- return 0;
- if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
- return 0;
+static const struct hwmon_channel_info max31790_pwm = {
+ .type = hwmon_pwm,
+ .config = max31790_pwm_config,
+};
- return a->mode;
-}
+static const struct hwmon_channel_info *max31790_info[] = {
+ &max31790_fan,
+ &max31790_pwm,
+ NULL
+};
+
+static const struct hwmon_ops max31790_hwmon_ops = {
+ .is_visible = max31790_is_visible,
+ .read = max31790_read,
+ .write = max31790_write,
+};
-static const struct attribute_group max31790_group = {
- .attrs = max31790_attrs,
- .is_visible = max31790_attrs_visible,
+static const struct hwmon_chip_info max31790_chip_info = {
+ .ops = &max31790_hwmon_ops,
+ .info = max31790_info,
};
-__ATTRIBUTE_GROUPS(max31790);
static int max31790_init_client(struct i2c_client *client,
struct max31790_data *data)
@@ -575,8 +500,10 @@ static int max31790_probe(struct i2c_client *client,
if (err)
return err;
- hwmon_dev = devm_hwmon_device_register_with_groups(dev,
- client->name, data, max31790_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &max31790_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index 162a520..a993b44 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -39,6 +39,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
+#include <linux/of_device.h>
/*
* Insmod parameters
@@ -48,7 +49,7 @@
static int fan_voltage;
/* prescaler: Possible values are 1, 2, 4, 8, 16 or 0 for don't change */
static int prescaler;
-/* clock: The clock frequency of the chip the driver should assume */
+/* clock: The clock frequency of the chip (max6651 can be clocked externally) */
static int clock = 254000;
module_param(fan_voltage, int, S_IRUGO);
@@ -133,6 +134,19 @@ static const u8 tach_reg[] = {
MAX6650_REG_TACH3,
};
+static const struct of_device_id max6650_dt_match[] = {
+ {
+ .compatible = "maxim,max6650",
+ .data = (void *)1
+ },
+ {
+ .compatible = "maxim,max6651",
+ .data = (void *)4
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max6650_dt_match);
+
static struct max6650_data *max6650_update_device(struct device *dev)
{
struct max6650_data *data = dev_get_drvdata(dev);
@@ -171,6 +185,30 @@ static struct max6650_data *max6650_update_device(struct device *dev)
return data;
}
+/*
+ * Change the operating mode of the chip (if needed).
+ * mode is one of the MAX6650_CFG_MODE_* values.
+ */
+static int max6650_set_operating_mode(struct max6650_data *data, u8 mode)
+{
+ int result;
+ u8 config = data->config;
+
+ if (mode == (config & MAX6650_CFG_MODE_MASK))
+ return 0;
+
+ config = (config & ~MAX6650_CFG_MODE_MASK) | mode;
+
+ result = i2c_smbus_write_byte_data(data->client, MAX6650_REG_CONFIG,
+ config);
+ if (result < 0)
+ return result;
+
+ data->config = config;
+
+ return 0;
+}
+
static ssize_t get_fan(struct device *dev, struct device_attribute *devattr,
char *buf)
{
@@ -252,18 +290,12 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
return sprintf(buf, "%d\n", rpm);
}
-static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static int max6650_set_target(struct max6650_data *data, unsigned long rpm)
{
- struct max6650_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
int kscale, ktach;
- unsigned long rpm;
- int err;
- err = kstrtoul(buf, 10, &rpm);
- if (err)
- return err;
+ if (rpm == 0)
+ return max6650_set_operating_mode(data, MAX6650_CFG_MODE_OFF);
rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
@@ -274,8 +306,6 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
* KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
*/
- mutex_lock(&data->update_lock);
-
kscale = DIV_FROM_REG(data->config);
ktach = ((clock * kscale) / (256 * rpm / 60)) - 1;
if (ktach < 0)
@@ -284,10 +314,30 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
ktach = 255;
data->speed = ktach;
- i2c_smbus_write_byte_data(client, MAX6650_REG_SPEED, data->speed);
+ return i2c_smbus_write_byte_data(data->client, MAX6650_REG_SPEED,
+ data->speed);
+}
+
+static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct max6650_data *data = dev_get_drvdata(dev);
+ unsigned long rpm;
+ int err;
+
+ err = kstrtoul(buf, 10, &rpm);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ err = max6650_set_target(data, rpm);
mutex_unlock(&data->update_lock);
+ if (err < 0)
+ return err;
+
return count;
}
@@ -341,12 +391,11 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
data->dac = 180 - (180 * pwm)/255;
else
data->dac = 76 - (76 * pwm)/255;
-
- i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
+ err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
/*
@@ -355,14 +404,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
* 0 = Fan always on
* 1 = Open loop, Voltage is set according to speed, not regulated.
* 2 = Closed loop, RPM for all fans regulated by fan1 tachometer
+ * 3 = Fan off
*/
-
static ssize_t get_enable(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct max6650_data *data = max6650_update_device(dev);
int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4;
- int sysfs_modes[4] = {0, 1, 2, 1};
+ int sysfs_modes[4] = {0, 3, 2, 1};
return sprintf(buf, "%d\n", sysfs_modes[mode]);
}
@@ -371,25 +420,25 @@ static ssize_t set_enable(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct max6650_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- int max6650_modes[3] = {0, 3, 2};
unsigned long mode;
int err;
+ const u8 max6650_modes[] = {
+ MAX6650_CFG_MODE_ON,
+ MAX6650_CFG_MODE_OPEN_LOOP,
+ MAX6650_CFG_MODE_CLOSED_LOOP,
+ MAX6650_CFG_MODE_OFF,
+ };
err = kstrtoul(buf, 10, &mode);
if (err)
return err;
- if (mode > 2)
+ if (mode >= ARRAY_SIZE(max6650_modes))
return -EINVAL;
mutex_lock(&data->update_lock);
- data->config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
- data->config = (data->config & ~MAX6650_CFG_MODE_MASK)
- | (max6650_modes[mode] << 4);
-
- i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, data->config);
+ max6650_set_operating_mode(data, max6650_modes[mode]);
mutex_unlock(&data->update_lock);
@@ -566,6 +615,18 @@ static int max6650_init_client(struct max6650_data *data,
struct device *dev = &client->dev;
int config;
int err = -EIO;
+ u32 voltage;
+ u32 prescale;
+ u32 target_rpm;
+
+ if (of_property_read_u32(dev->of_node, "maxim,fan-microvolt",
+ &voltage))
+ voltage = fan_voltage;
+ else
+ voltage /= 1000000; /* Microvolts to volts */
+ if (of_property_read_u32(dev->of_node, "maxim,fan-prescale",
+ &prescale))
+ prescale = prescaler;
config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
@@ -574,7 +635,7 @@ static int max6650_init_client(struct max6650_data *data,
return err;
}
- switch (fan_voltage) {
+ switch (voltage) {
case 0:
break;
case 5:
@@ -584,14 +645,10 @@ static int max6650_init_client(struct max6650_data *data,
config |= MAX6650_CFG_V12;
break;
default:
- dev_err(dev, "illegal value for fan_voltage (%d)\n",
- fan_voltage);
+ dev_err(dev, "illegal value for fan_voltage (%d)\n", voltage);
}
- dev_info(dev, "Fan voltage is set to %dV.\n",
- (config & MAX6650_CFG_V12) ? 12 : 5);
-
- switch (prescaler) {
+ switch (prescale) {
case 0:
break;
case 1:
@@ -614,28 +671,13 @@ static int max6650_init_client(struct max6650_data *data,
| MAX6650_CFG_PRESCALER_16;
break;
default:
- dev_err(dev, "illegal value for prescaler (%d)\n", prescaler);
+ dev_err(dev, "illegal value for prescaler (%d)\n", prescale);
}
- dev_info(dev, "Prescaler is set to %d.\n",
+ dev_info(dev, "Fan voltage: %dV, prescaler: %d.\n",
+ (config & MAX6650_CFG_V12) ? 12 : 5,
1 << (config & MAX6650_CFG_PRESCALER_MASK));
- /*
- * If mode is set to "full off", we change it to "open loop" and
- * set DAC to 255, which has the same effect. We do this because
- * there's no "full off" mode defined in hwmon specifications.
- */
-
- if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) {
- dev_dbg(dev, "Change mode to open loop, full off.\n");
- config = (config & ~MAX6650_CFG_MODE_MASK)
- | MAX6650_CFG_MODE_OPEN_LOOP;
- if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) {
- dev_err(dev, "DAC write error, aborting.\n");
- return err;
- }
- }
-
if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) {
dev_err(dev, "Config write error, aborting.\n");
return err;
@@ -644,6 +686,12 @@ static int max6650_init_client(struct max6650_data *data,
data->config = config;
data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT);
+ if (!of_property_read_u32(client->dev.of_node, "maxim,fan-target-rpm",
+ &target_rpm)) {
+ max6650_set_target(data, target_rpm);
+ max6650_set_operating_mode(data, MAX6650_CFG_MODE_CLOSED_LOOP);
+ }
+
return 0;
}
@@ -651,6 +699,8 @@ static int max6650_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
+ const struct of_device_id *of_id =
+ of_match_device(of_match_ptr(max6650_dt_match), dev);
struct max6650_data *data;
struct device *hwmon_dev;
int err;
@@ -661,7 +711,7 @@ static int max6650_probe(struct i2c_client *client,
data->client = client;
mutex_init(&data->update_lock);
- data->nr_fans = id->driver_data;
+ data->nr_fans = of_id ? (int)(uintptr_t)of_id->data : id->driver_data;
/*
* Initialize the max6650 chip
@@ -691,6 +741,7 @@ MODULE_DEVICE_TABLE(i2c, max6650_id);
static struct i2c_driver max6650_driver = {
.driver = {
.name = "max6650",
+ .of_match_table = of_match_ptr(max6650_dt_match),
},
.probe = max6650_probe,
.id_table = max6650_id,
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index d087a8e..ce75dd4 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -195,6 +195,8 @@ superio_exit(int ioreg)
#define NUM_FAN 6
+#define TEMP_SOURCE_VIRTUAL 0x1f
+
/* Common and NCT6775 specific data */
/* Voltage min/max registers for nr=7..14 are in bank 5 */
@@ -3940,7 +3942,7 @@ static int nct6775_probe(struct platform_device *pdev)
continue;
src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f;
- if (!src || (mask & (1 << src)))
+ if (!src)
continue;
if (src >= data->temp_label_num ||
@@ -3952,7 +3954,16 @@ static int nct6775_probe(struct platform_device *pdev)
continue;
}
- mask |= 1 << src;
+ /*
+ * For virtual temperature sources, the 'virtual' temperature
+ * for each fan reflects a different temperature, and there
+ * are no duplicates.
+ */
+ if (src != TEMP_SOURCE_VIRTUAL) {
+ if (mask & (1 << src))
+ continue;
+ mask |= 1 << src;
+ }
/* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
if (src <= data->temp_fixed_num) {
@@ -4232,11 +4243,11 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
if (err)
return err;
- if (force_id)
+ val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) |
+ superio_inb(sioaddr, SIO_REG_DEVID + 1);
+ if (force_id && val != 0xffff)
val = force_id;
- else
- val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
- | superio_inb(sioaddr, SIO_REG_DEVID + 1);
+
switch (val & SIO_ID_MASK) {
case SIO_NCT6106_ID:
sio_data->kind = nct6106;
diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c
index 08ff89d..95a68ab 100644
--- a/drivers/hwmon/nct7904.c
+++ b/drivers/hwmon/nct7904.c
@@ -21,7 +21,6 @@
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#define VENDOR_ID_REG 0x7A /* Any bank */
#define NUVOTON_ID 0x50
@@ -153,341 +152,230 @@ static int nct7904_write_reg(struct nct7904_data *data,
return ret;
}
-/* FANIN ATTR */
-static ssize_t show_fan(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
+ long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
+ unsigned int cnt, rpm;
int ret;
- unsigned cnt, rpm;
- ret = nct7904_read_reg16(data, BANK_0, FANIN1_HV_REG + index * 2);
- if (ret < 0)
- return ret;
- cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
- if (cnt == 0x1fff)
- rpm = 0;
- else
- rpm = 1350000 / cnt;
- return sprintf(buf, "%u\n", rpm);
+ switch(attr) {
+ case hwmon_fan_input:
+ ret = nct7904_read_reg16(data, BANK_0,
+ FANIN1_HV_REG + channel * 2);
+ if (ret < 0)
+ return ret;
+ cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
+ if (cnt == 0x1fff)
+ rpm = 0;
+ else
+ rpm = 1350000 / cnt;
+ *val = rpm;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static umode_t nct7904_fanin_is_visible(struct kobject *kobj,
- struct attribute *a, int n)
+static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct7904_data *data = dev_get_drvdata(dev);
+ const struct nct7904_data *data = _data;
- if (data->fanin_mask & (1 << n))
- return a->mode;
+ if (attr == hwmon_fan_input && data->fanin_mask & (1 << channel))
+ return S_IRUGO;
return 0;
}
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3);
-static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4);
-static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5);
-static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6);
-static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7);
-static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_fan, NULL, 8);
-static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, show_fan, NULL, 9);
-static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, show_fan, NULL, 10);
-static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, show_fan, NULL, 11);
-
-static struct attribute *nct7904_fanin_attrs[] = {
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- &sensor_dev_attr_fan2_input.dev_attr.attr,
- &sensor_dev_attr_fan3_input.dev_attr.attr,
- &sensor_dev_attr_fan4_input.dev_attr.attr,
- &sensor_dev_attr_fan5_input.dev_attr.attr,
- &sensor_dev_attr_fan6_input.dev_attr.attr,
- &sensor_dev_attr_fan7_input.dev_attr.attr,
- &sensor_dev_attr_fan8_input.dev_attr.attr,
- &sensor_dev_attr_fan9_input.dev_attr.attr,
- &sensor_dev_attr_fan10_input.dev_attr.attr,
- &sensor_dev_attr_fan11_input.dev_attr.attr,
- &sensor_dev_attr_fan12_input.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group nct7904_fanin_group = {
- .attrs = nct7904_fanin_attrs,
- .is_visible = nct7904_fanin_is_visible,
+static u8 nct7904_chan_to_index[] = {
+ 0, /* Not used */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 18, 19, 20, 16
};
-/* VSEN ATTR */
-static ssize_t show_voltage(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_read_in(struct device *dev, u32 attr, int channel,
+ long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
- int ret;
- int volt;
+ int ret, volt, index;
- ret = nct7904_read_reg16(data, BANK_0, VSEN1_HV_REG + index * 2);
- if (ret < 0)
- return ret;
- volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
- if (index < 14)
- volt *= 2; /* 0.002V scale */
- else
- volt *= 6; /* 0.006V scale */
+ index = nct7904_chan_to_index[channel];
- return sprintf(buf, "%d\n", volt);
+ switch(attr) {
+ case hwmon_in_input:
+ ret = nct7904_read_reg16(data, BANK_0,
+ VSEN1_HV_REG + index * 2);
+ if (ret < 0)
+ return ret;
+ volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ if (index < 14)
+ volt *= 2; /* 0.002V scale */
+ else
+ volt *= 6; /* 0.006V scale */
+ *val = volt;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_ltemp(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel)
{
- struct nct7904_data *data = dev_get_drvdata(dev);
- int ret;
- int temp;
+ const struct nct7904_data *data = _data;
+ int index = nct7904_chan_to_index[channel];
- ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
- if (ret < 0)
- return ret;
- temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
- temp = sign_extend32(temp, 10) * 125;
-
- return sprintf(buf, "%d\n", temp);
-}
-
-static umode_t nct7904_vsen_is_visible(struct kobject *kobj,
- struct attribute *a, int n)
-{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct7904_data *data = dev_get_drvdata(dev);
+ if (channel > 0 && attr == hwmon_in_input &&
+ (data->vsen_mask & BIT(index)))
+ return S_IRUGO;
- if (data->vsen_mask & (1 << n))
- return a->mode;
return 0;
}
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0);
-static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1);
-static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2);
-static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3);
-static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4);
-static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5);
-static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6);
-static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7);
-static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8);
-static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9);
-static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10);
-static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11);
-static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12);
-static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, show_voltage, NULL, 13);
-/*
- * Next 3 voltage sensors have specific names in the Nuvoton doc
- * (3VDD, VBAT, 3VSB) but we use vacant numbers for them.
- */
-static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, show_voltage, NULL, 14);
-static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_voltage, NULL, 15);
-static SENSOR_DEVICE_ATTR(in20_input, S_IRUGO, show_voltage, NULL, 16);
-/* This is not a voltage, but a local temperature sensor. */
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_ltemp, NULL, 0);
-static SENSOR_DEVICE_ATTR(in17_input, S_IRUGO, show_voltage, NULL, 18);
-static SENSOR_DEVICE_ATTR(in18_input, S_IRUGO, show_voltage, NULL, 19);
-static SENSOR_DEVICE_ATTR(in19_input, S_IRUGO, show_voltage, NULL, 20);
-
-static struct attribute *nct7904_vsen_attrs[] = {
- &sensor_dev_attr_in1_input.dev_attr.attr,
- &sensor_dev_attr_in2_input.dev_attr.attr,
- &sensor_dev_attr_in3_input.dev_attr.attr,
- &sensor_dev_attr_in4_input.dev_attr.attr,
- &sensor_dev_attr_in5_input.dev_attr.attr,
- &sensor_dev_attr_in6_input.dev_attr.attr,
- &sensor_dev_attr_in7_input.dev_attr.attr,
- &sensor_dev_attr_in8_input.dev_attr.attr,
- &sensor_dev_attr_in9_input.dev_attr.attr,
- &sensor_dev_attr_in10_input.dev_attr.attr,
- &sensor_dev_attr_in11_input.dev_attr.attr,
- &sensor_dev_attr_in12_input.dev_attr.attr,
- &sensor_dev_attr_in13_input.dev_attr.attr,
- &sensor_dev_attr_in14_input.dev_attr.attr,
- &sensor_dev_attr_in15_input.dev_attr.attr,
- &sensor_dev_attr_in16_input.dev_attr.attr,
- &sensor_dev_attr_in20_input.dev_attr.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_in17_input.dev_attr.attr,
- &sensor_dev_attr_in18_input.dev_attr.attr,
- &sensor_dev_attr_in19_input.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group nct7904_vsen_group = {
- .attrs = nct7904_vsen_attrs,
- .is_visible = nct7904_vsen_is_visible,
-};
-
-/* CPU_TEMP ATTR */
-static ssize_t show_tcpu(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
- int ret;
- int temp;
-
- ret = nct7904_read_reg16(data, BANK_0, T_CPU1_HV_REG + index * 2);
- if (ret < 0)
- return ret;
-
- temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
- temp = sign_extend32(temp, 10) * 125;
- return sprintf(buf, "%d\n", temp);
+ int ret, temp;
+
+ switch(attr) {
+ case hwmon_temp_input:
+ if (channel == 0)
+ ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
+ else
+ ret = nct7904_read_reg16(data, BANK_0,
+ T_CPU1_HV_REG + (channel - 1) * 2);
+ if (ret < 0)
+ return ret;
+ temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ *val = sign_extend32(temp, 10) * 125;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static umode_t nct7904_tcpu_is_visible(struct kobject *kobj,
- struct attribute *a, int n)
+static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct7904_data *data = dev_get_drvdata(dev);
+ const struct nct7904_data *data = _data;
+
+ if (attr == hwmon_temp_input) {
+ if (channel == 0) {
+ if (data->vsen_mask & BIT(17))
+ return S_IRUGO;
+ } else {
+ if (data->tcpu_mask & BIT(channel - 1))
+ return S_IRUGO;
+ }
+ }
- if (data->tcpu_mask & (1 << n))
- return a->mode;
return 0;
}
-/* "temp1_input" reserved for local temp */
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_tcpu, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_tcpu, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_tcpu, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_tcpu, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_tcpu, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_tcpu, NULL, 5);
-static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_tcpu, NULL, 6);
-static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_tcpu, NULL, 7);
-
-static struct attribute *nct7904_tcpu_attrs[] = {
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp5_input.dev_attr.attr,
- &sensor_dev_attr_temp6_input.dev_attr.attr,
- &sensor_dev_attr_temp7_input.dev_attr.attr,
- &sensor_dev_attr_temp8_input.dev_attr.attr,
- &sensor_dev_attr_temp9_input.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group nct7904_tcpu_group = {
- .attrs = nct7904_tcpu_attrs,
- .is_visible = nct7904_tcpu_is_visible,
-};
-
-/* PWM ATTR */
-static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
+ long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
- unsigned long val;
int ret;
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
- if (val > 255)
- return -EINVAL;
-
- ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + index, val);
+ switch(attr) {
+ case hwmon_pwm_input:
+ ret = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + channel);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return 0;
+ case hwmon_pwm_enable:
+ ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + channel);
+ if (ret < 0)
+ return ret;
- return ret ? ret : count;
+ *val = ret ? 2 : 1;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_pwm(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
+ long val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
- int val;
-
- val = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + index);
- if (val < 0)
- return val;
+ int ret;
- return sprintf(buf, "%d\n", val);
+ switch(attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > 255)
+ return -EINVAL;
+ ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + channel,
+ val);
+ return ret;
+ case hwmon_pwm_enable:
+ if (val < 1 || val > 2 ||
+ (val == 2 && !data->fan_mode[channel]))
+ return -EINVAL;
+ ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + channel,
+ val == 2 ? data->fan_mode[channel] : 0);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t store_enable(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static umode_t nct7904_pwm_is_visible(const void *_data, u32 attr, int channel)
{
- int index = to_sensor_dev_attr(devattr)->index;
- struct nct7904_data *data = dev_get_drvdata(dev);
- unsigned long val;
- int ret;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
- if (val < 1 || val > 2 || (val == 2 && !data->fan_mode[index]))
- return -EINVAL;
-
- ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + index,
- val == 2 ? data->fan_mode[index] : 0);
-
- return ret ? ret : count;
+ switch(attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_enable:
+ return S_IRUGO | S_IWUSR;
+ default:
+ return 0;
+ }
}
-/* Return 1 for manual mode or 2 for SmartFan mode */
-static ssize_t show_enable(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
- struct nct7904_data *data = dev_get_drvdata(dev);
- int val;
-
- val = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + index);
- if (val < 0)
- return val;
-
- return sprintf(buf, "%d\n", val ? 2 : 1);
+ switch (type) {
+ case hwmon_in:
+ return nct7904_read_in(dev, attr, channel, val);
+ case hwmon_fan:
+ return nct7904_read_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return nct7904_read_pwm(dev, attr, channel, val);
+ case hwmon_temp:
+ return nct7904_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-/* 2 attributes per channel: pwm and mode */
-static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR,
- show_pwm, store_pwm, 0);
-static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
- show_enable, store_enable, 0);
-static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR,
- show_pwm, store_pwm, 1);
-static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
- show_enable, store_enable, 1);
-static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR,
- show_pwm, store_pwm, 2);
-static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR,
- show_enable, store_enable, 2);
-static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR,
- show_pwm, store_pwm, 3);
-static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR,
- show_enable, store_enable, 3);
-
-static struct attribute *nct7904_fanctl_attrs[] = {
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_pwm1_enable.dev_attr.attr,
- &sensor_dev_attr_pwm2.dev_attr.attr,
- &sensor_dev_attr_pwm2_enable.dev_attr.attr,
- &sensor_dev_attr_pwm3.dev_attr.attr,
- &sensor_dev_attr_pwm3_enable.dev_attr.attr,
- &sensor_dev_attr_pwm4.dev_attr.attr,
- &sensor_dev_attr_pwm4_enable.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group nct7904_fanctl_group = {
- .attrs = nct7904_fanctl_attrs,
-};
+static int nct7904_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return nct7904_write_pwm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
-static const struct attribute_group *nct7904_groups[] = {
- &nct7904_fanin_group,
- &nct7904_vsen_group,
- &nct7904_tcpu_group,
- &nct7904_fanctl_group,
- NULL
-};
+static umode_t nct7904_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_in:
+ return nct7904_in_is_visible(data, attr, channel);
+ case hwmon_fan:
+ return nct7904_fan_is_visible(data, attr, channel);
+ case hwmon_pwm:
+ return nct7904_pwm_is_visible(data, attr, channel);
+ case hwmon_temp:
+ return nct7904_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int nct7904_detect(struct i2c_client *client,
@@ -512,6 +400,103 @@ static int nct7904_detect(struct i2c_client *client,
return 0;
}
+static const u32 nct7904_in_config[] = {
+ HWMON_I_INPUT, /* dummy, skipped in is_visible */
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info nct7904_in = {
+ .type = hwmon_in,
+ .config = nct7904_in_config,
+};
+
+static const u32 nct7904_fan_config[] = {
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info nct7904_fan = {
+ .type = hwmon_fan,
+ .config = nct7904_fan_config,
+};
+
+static const u32 nct7904_pwm_config[] = {
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ 0
+};
+
+static const struct hwmon_channel_info nct7904_pwm = {
+ .type = hwmon_pwm,
+ .config = nct7904_pwm_config,
+};
+
+static const u32 nct7904_temp_config[] = {
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info nct7904_temp = {
+ .type = hwmon_temp,
+ .config = nct7904_temp_config,
+};
+
+static const struct hwmon_channel_info *nct7904_info[] = {
+ &nct7904_in,
+ &nct7904_fan,
+ &nct7904_pwm,
+ &nct7904_temp,
+ NULL
+};
+
+static const struct hwmon_ops nct7904_hwmon_ops = {
+ .is_visible = nct7904_is_visible,
+ .read = nct7904_read,
+ .write = nct7904_write,
+};
+
+static const struct hwmon_chip_info nct7904_chip_info = {
+ .ops = &nct7904_hwmon_ops,
+ .info = nct7904_info,
+};
+
static int nct7904_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -566,8 +551,8 @@ static int nct7904_probe(struct i2c_client *client,
}
hwmon_dev =
- devm_hwmon_device_register_with_groups(dev, client->name, data,
- nct7904_groups);
+ devm_hwmon_device_register_with_info(dev, client->name, data,
+ &nct7904_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index 8ef7b71..c52d07c 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -253,12 +253,9 @@ static const struct ntc_compensation b57330v2103[] = {
};
struct ntc_data {
- struct device *hwmon_dev;
struct ntc_thermistor_platform_data *pdata;
const struct ntc_compensation *comp;
- struct device *dev;
int n_comp;
- char name[PLATFORM_NAME_SIZE];
};
#if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO)
@@ -316,22 +313,22 @@ static const struct of_device_id ntc_match[] = {
MODULE_DEVICE_TABLE(of, ntc_match);
static struct ntc_thermistor_platform_data *
-ntc_thermistor_parse_dt(struct platform_device *pdev)
+ntc_thermistor_parse_dt(struct device *dev)
{
struct iio_channel *chan;
enum iio_chan_type type;
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = dev->of_node;
struct ntc_thermistor_platform_data *pdata;
int ret;
if (!np)
return NULL;
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
- chan = iio_channel_get(&pdev->dev, NULL);
+ chan = devm_iio_channel_get(dev, NULL);
if (IS_ERR(chan))
return ERR_CAST(chan);
@@ -359,22 +356,15 @@ ntc_thermistor_parse_dt(struct platform_device *pdev)
return pdata;
}
-static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
-{
- if (pdata->chan)
- iio_channel_release(pdata->chan);
-}
#else
static struct ntc_thermistor_platform_data *
-ntc_thermistor_parse_dt(struct platform_device *pdev)
+ntc_thermistor_parse_dt(struct device *dev)
{
return NULL;
}
#define ntc_match NULL
-static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
-{ }
#endif
static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
@@ -516,9 +506,8 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data)
return -EINVAL;
}
-static int ntc_read_temp(void *dev, int *temp)
+static int ntc_read_temp(void *data, int *temp)
{
- struct ntc_data *data = dev_get_drvdata(dev);
int ohm;
ohm = ntc_thermistor_get_ohm(data);
@@ -530,14 +519,6 @@ static int ntc_read_temp(void *dev, int *temp)
return 0;
}
-static ssize_t ntc_show_name(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ntc_data *data = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", data->name);
-}
-
static ssize_t ntc_show_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -559,18 +540,13 @@ static ssize_t ntc_show_temp(struct device *dev,
static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0);
-static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL);
-static struct attribute *ntc_attributes[] = {
- &dev_attr_name.attr,
+static struct attribute *ntc_attrs[] = {
&sensor_dev_attr_temp1_type.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
-
-static const struct attribute_group ntc_attr_group = {
- .attrs = ntc_attributes,
-};
+ATTRIBUTE_GROUPS(ntc);
static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
.get_temp = ntc_read_temp,
@@ -579,33 +555,34 @@ static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
static int ntc_thermistor_probe(struct platform_device *pdev)
{
struct thermal_zone_device *tz;
+ struct device *dev = &pdev->dev;
const struct of_device_id *of_id =
- of_match_device(of_match_ptr(ntc_match), &pdev->dev);
+ of_match_device(of_match_ptr(ntc_match), dev);
const struct platform_device_id *pdev_id;
struct ntc_thermistor_platform_data *pdata;
+ struct device *hwmon_dev;
struct ntc_data *data;
- int ret;
- pdata = ntc_thermistor_parse_dt(pdev);
+ pdata = ntc_thermistor_parse_dt(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
else if (pdata == NULL)
- pdata = dev_get_platdata(&pdev->dev);
+ pdata = dev_get_platdata(dev);
if (!pdata) {
- dev_err(&pdev->dev, "No platform init data supplied.\n");
+ dev_err(dev, "No platform init data supplied.\n");
return -ENODEV;
}
/* Either one of the two is required. */
if (!pdata->read_uv && !pdata->read_ohm) {
- dev_err(&pdev->dev,
+ dev_err(dev,
"Both read_uv and read_ohm missing. Need either one of the two.\n");
return -EINVAL;
}
if (pdata->read_uv && pdata->read_ohm) {
- dev_warn(&pdev->dev,
+ dev_warn(dev,
"Only one of read_uv and read_ohm is needed; ignoring read_uv.\n");
pdata->read_uv = NULL;
}
@@ -617,20 +594,17 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
NTC_CONNECTED_POSITIVE) ||
(pdata->connect != NTC_CONNECTED_POSITIVE &&
pdata->connect != NTC_CONNECTED_GROUND))) {
- dev_err(&pdev->dev,
- "Required data to use read_uv not supplied.\n");
+ dev_err(dev, "Required data to use read_uv not supplied.\n");
return -EINVAL;
}
- data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct ntc_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
- data->dev = &pdev->dev;
data->pdata = pdata;
- strlcpy(data->name, pdev_id->name, sizeof(data->name));
switch (pdev_id->driver_data) {
case TYPE_NCPXXWB473:
@@ -654,49 +628,25 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
data->n_comp = ARRAY_SIZE(ncpXXxh103);
break;
default:
- dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
+ dev_err(dev, "Unknown device type: %lu(%s)\n",
pdev_id->driver_data, pdev_id->name);
return -EINVAL;
}
- platform_set_drvdata(pdev, data);
-
- ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group);
- if (ret) {
- dev_err(data->dev, "unable to create sysfs files\n");
- return ret;
- }
-
- data->hwmon_dev = hwmon_device_register(data->dev);
- if (IS_ERR(data->hwmon_dev)) {
- dev_err(data->dev, "unable to register as hwmon device.\n");
- ret = PTR_ERR(data->hwmon_dev);
- goto err_after_sysfs;
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, pdev_id->name,
+ data, ntc_groups);
+ if (IS_ERR(hwmon_dev)) {
+ dev_err(dev, "unable to register as hwmon device.\n");
+ return PTR_ERR(hwmon_dev);
}
- dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n",
- pdev_id->name);
+ dev_info(dev, "Thermistor type: %s successfully probed.\n",
+ pdev_id->name);
- tz = devm_thermal_zone_of_sensor_register(data->dev, 0, data->dev,
+ tz = devm_thermal_zone_of_sensor_register(dev, 0, data,
&ntc_of_thermal_ops);
if (IS_ERR(tz))
- dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n");
-
- return 0;
-err_after_sysfs:
- sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
- ntc_iio_channel_release(pdata);
- return ret;
-}
-
-static int ntc_thermistor_remove(struct platform_device *pdev)
-{
- struct ntc_data *data = platform_get_drvdata(pdev);
- struct ntc_thermistor_platform_data *pdata = data->pdata;
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
- ntc_iio_channel_release(pdata);
+ dev_dbg(dev, "Failed to register to thermal fw.\n");
return 0;
}
@@ -707,7 +657,6 @@ static struct platform_driver ntc_thermistor_driver = {
.of_match_table = of_match_ptr(ntc_match),
},
.probe = ntc_thermistor_probe,
- .remove = ntc_thermistor_remove,
.id_table = ntc_thermistor_id,
};
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 054d3d8..cad1229 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -126,12 +126,12 @@ config SENSORS_TPS40422
be called tps40422.
config SENSORS_UCD9000
- tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
+ tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910"
default n
help
If you say yes here you get hardware monitoring support for TI
- UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
- Controllers.
+ UCD90120, UCD90124, UCD90160, UCD9090, UCD90910, Sequencer and System
+ Health Controllers.
This driver can also be built as a module. If so, the module will
be called ucd9000.
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index 0a74991..44ca8a9 100644
--- a/drivers/hwmon/pmbus/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
+#include <linux/i2c/pmbus.h>
#include "pmbus.h"
/*
@@ -167,14 +168,26 @@ static int pmbus_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pmbus_driver_info *info;
+ struct pmbus_platform_data *pdata = NULL;
+ struct device *dev = &client->dev;
- info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
- GFP_KERNEL);
+ info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
+ if (!strcmp(id->name, "dps460") || !strcmp(id->name, "dps800") ||
+ !strcmp(id->name, "sgd009")) {
+ pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->flags = PMBUS_SKIP_STATUS_CHECK;
+ }
+
info->pages = id->driver_data;
info->identify = pmbus_identify;
+ dev->platform_data = pdata;
return pmbus_do_probe(client, id, info);
}
@@ -186,6 +199,8 @@ static const struct i2c_device_id pmbus_id[] = {
{"adp4000", 1},
{"bmr453", 1},
{"bmr454", 1},
+ {"dps460", 1},
+ {"dps800", 1},
{"mdt040", 1},
{"ncp4200", 1},
{"ncp4208", 1},
@@ -193,6 +208,7 @@ static const struct i2c_device_id pmbus_id[] = {
{"pdt006", 1},
{"pdt012", 1},
{"pmbus", 0},
+ {"sgd009", 1},
{"tps40400", 1},
{"tps544b20", 1},
{"tps544b25", 1},
diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index fbb1479..3e3aa95 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -28,7 +28,7 @@
#include <linux/i2c/pmbus.h>
#include "pmbus.h"
-enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 };
+enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
#define UCD9000_MONITOR_CONFIG 0xd5
#define UCD9000_NUM_PAGES 0xd6
@@ -112,6 +112,7 @@ static const struct i2c_device_id ucd9000_id[] = {
{"ucd9000", ucd9000},
{"ucd90120", ucd90120},
{"ucd90124", ucd90124},
+ {"ucd90160", ucd90160},
{"ucd9090", ucd9090},
{"ucd90910", ucd90910},
{}
diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c
index 25b44e6..559a3dc 100644
--- a/drivers/hwmon/scpi-hwmon.c
+++ b/drivers/hwmon/scpi-hwmon.c
@@ -255,7 +255,6 @@ static const struct of_device_id scpi_of_match[] = {
static struct platform_driver scpi_hwmon_platdrv = {
.driver = {
.name = "scpi-hwmon",
- .owner = THIS_MODULE,
.of_match_table = scpi_of_match,
},
.probe = scpi_hwmon_probe,
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 8479ac5..36bba2a 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -25,7 +25,6 @@
#include <linux/device.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
-#include <linux/thermal.h>
#include <linux/of.h>
#define DRIVER_NAME "tmp102"
@@ -79,84 +78,113 @@ static inline u16 tmp102_mC_to_reg(int val)
return (val * 128) / 1000;
}
-static int tmp102_read_temp(void *dev, int *temp)
+static int tmp102_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
{
struct tmp102 *tmp102 = dev_get_drvdata(dev);
- unsigned int reg;
- int ret;
-
- if (time_before(jiffies, tmp102->ready_time)) {
- dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
- return -EAGAIN;
+ unsigned int regval;
+ int err, reg;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ /* Is it too early to return a conversion ? */
+ if (time_before(jiffies, tmp102->ready_time)) {
+ dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
+ return -EAGAIN;
+ }
+ reg = TMP102_TEMP_REG;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = TMP102_TLOW_REG;
+ break;
+ case hwmon_temp_max:
+ reg = TMP102_THIGH_REG;
+ break;
+ default:
+ return -EOPNOTSUPP;
}
- ret = regmap_read(tmp102->regmap, TMP102_TEMP_REG, &reg);
- if (ret < 0)
- return ret;
-
- *temp = tmp102_reg_to_mC(reg);
+ err = regmap_read(tmp102->regmap, reg, &regval);
+ if (err < 0)
+ return err;
+ *temp = tmp102_reg_to_mC(regval);
return 0;
}
-static ssize_t tmp102_show_temp(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int tmp102_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long temp)
{
- struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
struct tmp102 *tmp102 = dev_get_drvdata(dev);
- int regaddr = sda->index;
- unsigned int reg;
- int err;
-
- if (regaddr == TMP102_TEMP_REG &&
- time_before(jiffies, tmp102->ready_time))
- return -EAGAIN;
-
- err = regmap_read(tmp102->regmap, regaddr, &reg);
- if (err < 0)
- return err;
+ int reg;
+
+ switch (attr) {
+ case hwmon_temp_max_hyst:
+ reg = TMP102_TLOW_REG;
+ break;
+ case hwmon_temp_max:
+ reg = TMP102_THIGH_REG;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
- return sprintf(buf, "%d\n", tmp102_reg_to_mC(reg));
+ temp = clamp_val(temp, -256000, 255000);
+ return regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(temp));
}
-static ssize_t tmp102_set_temp(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static umode_t tmp102_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
- struct tmp102 *tmp102 = dev_get_drvdata(dev);
- int reg = sda->index;
- long val;
- int err;
-
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
- val = clamp_val(val, -256000, 255000);
-
- err = regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(val));
- return err ? : count;
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return S_IRUGO;
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_max:
+ return S_IRUGO | S_IWUSR;
+ default:
+ return 0;
+ }
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL,
- TMP102_TEMP_REG);
+static u32 tmp102_chip_config[] = {
+ HWMON_C_REGISTER_TZ,
+ 0
+};
+
+static const struct hwmon_channel_info tmp102_chip = {
+ .type = hwmon_chip,
+ .config = tmp102_chip_config,
+};
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp,
- tmp102_set_temp, TMP102_TLOW_REG);
+static u32 tmp102_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST,
+ 0
+};
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp,
- tmp102_set_temp, TMP102_THIGH_REG);
+static const struct hwmon_channel_info tmp102_temp = {
+ .type = hwmon_temp,
+ .config = tmp102_temp_config,
+};
-static struct attribute *tmp102_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
+static const struct hwmon_channel_info *tmp102_info[] = {
+ &tmp102_chip,
+ &tmp102_temp,
NULL
};
-ATTRIBUTE_GROUPS(tmp102);
-static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = {
- .get_temp = tmp102_read_temp,
+static const struct hwmon_ops tmp102_hwmon_ops = {
+ .is_visible = tmp102_is_visible,
+ .read = tmp102_read,
+ .write = tmp102_write,
+};
+
+static const struct hwmon_chip_info tmp102_chip_info = {
+ .ops = &tmp102_hwmon_ops,
+ .info = tmp102_info,
};
static void tmp102_restore_config(void *data)
@@ -188,7 +216,7 @@ static const struct regmap_config tmp102_regmap_config = {
};
static int tmp102_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
@@ -249,16 +277,14 @@ static int tmp102_probe(struct i2c_client *client,
tmp102->ready_time += msecs_to_jiffies(CONVERSION_TIME_MS);
}
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- tmp102,
- tmp102_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ tmp102,
+ &tmp102_chip_info,
+ NULL);
if (IS_ERR(hwmon_dev)) {
dev_dbg(dev, "unable to register hwmon device\n");
return PTR_ERR(hwmon_dev);
}
- devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
- &tmp102_of_thermal_ops);
-
dev_info(dev, "initialized\n");
return 0;
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
index 85d48d8..bfb98b9 100644
--- a/drivers/hwmon/tmp421.c
+++ b/drivers/hwmon/tmp421.c
@@ -72,6 +72,10 @@ MODULE_DEVICE_TABLE(i2c, tmp421_id);
struct tmp421_data {
struct i2c_client *client;
struct mutex update_lock;
+ u32 temp_config[5];
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[2];
+ struct hwmon_chip_info chip;
char valid;
unsigned long last_updated;
int channels;
@@ -125,85 +129,46 @@ static struct tmp421_data *tmp421_update_device(struct device *dev)
return data;
}
-static ssize_t show_temp_value(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int tmp421_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp421_data *data = tmp421_update_device(dev);
- int temp;
-
- mutex_lock(&data->update_lock);
- if (data->config & TMP421_CONFIG_RANGE)
- temp = temp_from_u16(data->temp[index]);
- else
- temp = temp_from_s16(data->temp[index]);
- mutex_unlock(&data->update_lock);
-
- return sprintf(buf, "%d\n", temp);
-}
+ struct tmp421_data *tmp421 = tmp421_update_device(dev);
+
+ switch (attr) {
+ case hwmon_temp_input:
+ if (tmp421->config & TMP421_CONFIG_RANGE)
+ *val = temp_from_u16(tmp421->temp[channel]);
+ else
+ *val = temp_from_s16(tmp421->temp[channel]);
+ return 0;
+ case hwmon_temp_fault:
+ /*
+ * The OPEN bit signals a fault. This is bit 0 of the temperature
+ * register (low byte).
+ */
+ *val = tmp421->temp[channel] & 0x01;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
-static ssize_t show_fault(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp421_data *data = tmp421_update_device(dev);
-
- /*
- * The OPEN bit signals a fault. This is bit 0 of the temperature
- * register (low byte).
- */
- if (data->temp[index] & 0x01)
- return sprintf(buf, "1\n");
- else
- return sprintf(buf, "0\n");
}
-static umode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a,
- int n)
+static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct tmp421_data *data = dev_get_drvdata(dev);
- struct device_attribute *devattr;
- unsigned int index;
-
- devattr = container_of(a, struct device_attribute, attr);
- index = to_sensor_dev_attr(devattr)->index;
-
- if (index < data->channels)
- return a->mode;
-
- return 0;
+ switch (attr) {
+ case hwmon_temp_fault:
+ if (channel == 0)
+ return 0;
+ return S_IRUGO;
+ case hwmon_temp_input:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_value, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_value, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
-
-static struct attribute *tmp421_attr[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp3_fault.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp4_fault.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group tmp421_group = {
- .attrs = tmp421_attr,
- .is_visible = tmp421_is_visible,
-};
-
-static const struct attribute_group *tmp421_groups[] = {
- &tmp421_group,
- NULL
-};
-
static int tmp421_init_client(struct i2c_client *client)
{
int config, config_orig;
@@ -289,13 +254,18 @@ static int tmp421_detect(struct i2c_client *client,
return 0;
}
+static const struct hwmon_ops tmp421_ops = {
+ .is_visible = tmp421_is_visible,
+ .read = tmp421_read,
+};
+
static int tmp421_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct tmp421_data *data;
- int err;
+ int i, err;
data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL);
if (!data)
@@ -309,8 +279,21 @@ static int tmp421_probe(struct i2c_client *client,
if (err)
return err;
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data, tmp421_groups);
+ for (i = 0; i < data->channels; i++)
+ data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT;
+
+ data->chip.ops = &tmp421_ops;
+ data->chip.info = data->info;
+
+ data->info[0] = &data->temp_info;
+
+ data->temp_info.type = hwmon_temp;
+ data->temp_info.config = data->temp_config;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &data->chip,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c
new file mode 100644
index 0000000..9c0dbb8
--- /dev/null
+++ b/drivers/hwmon/xgene-hwmon.c
@@ -0,0 +1,787 @@
+/*
+ * APM X-Gene SoC Hardware Monitoring Driver
+ *
+ * Copyright (c) 2016, Applied Micro Circuits Corporation
+ * Author: Loc Ho <lho@apm.com>
+ * Hoan Tran <hotran@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver provides the following features:
+ * - Retrieve CPU total power (uW)
+ * - Retrieve IO total power (uW)
+ * - Retrieve SoC temperature (milli-degree C) and alarm
+ */
+#include <linux/acpi.h>
+#include <linux/dma-mapping.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <acpi/pcc.h>
+
+/* SLIMpro message defines */
+#define MSG_TYPE_DBG 0
+#define MSG_TYPE_ERR 7
+#define MSG_TYPE_PWRMGMT 9
+
+#define MSG_TYPE(v) (((v) & 0xF0000000) >> 28)
+#define MSG_TYPE_SET(v) (((v) << 28) & 0xF0000000)
+#define MSG_SUBTYPE(v) (((v) & 0x0F000000) >> 24)
+#define MSG_SUBTYPE_SET(v) (((v) << 24) & 0x0F000000)
+
+#define DBG_SUBTYPE_SENSOR_READ 4
+#define SENSOR_RD_MSG 0x04FFE902
+#define SENSOR_RD_EN_ADDR(a) ((a) & 0x000FFFFF)
+#define PMD_PWR_REG 0x20
+#define PMD_PWR_MW_REG 0x26
+#define SOC_PWR_REG 0x21
+#define SOC_PWR_MW_REG 0x27
+#define SOC_TEMP_REG 0x10
+
+#define TEMP_NEGATIVE_BIT 8
+#define SENSOR_INVALID_DATA BIT(15)
+
+#define PWRMGMT_SUBTYPE_TPC 1
+#define TPC_ALARM 2
+#define TPC_GET_ALARM 3
+#define TPC_CMD(v) (((v) & 0x00FF0000) >> 16)
+#define TPC_CMD_SET(v) (((v) << 16) & 0x00FF0000)
+#define TPC_EN_MSG(hndl, cmd, type) \
+ (MSG_TYPE_SET(MSG_TYPE_PWRMGMT) | \
+ MSG_SUBTYPE_SET(hndl) | TPC_CMD_SET(cmd) | type)
+
+/* PCC defines */
+#define PCC_SIGNATURE_MASK 0x50424300
+#define PCCC_GENERATE_DB_INT BIT(15)
+#define PCCS_CMD_COMPLETE BIT(0)
+#define PCCS_SCI_DOORBEL BIT(1)
+#define PCCS_PLATFORM_NOTIFICATION BIT(3)
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define PCC_NUM_RETRIES 500
+
+#define ASYNC_MSG_FIFO_SIZE 16
+#define MBOX_OP_TIMEOUTMS 1000
+
+#define WATT_TO_mWATT(x) ((x) * 1000)
+#define mWATT_TO_uWATT(x) ((x) * 1000)
+#define CELSIUS_TO_mCELSIUS(x) ((x) * 1000)
+
+#define to_xgene_hwmon_dev(cl) \
+ container_of(cl, struct xgene_hwmon_dev, mbox_client)
+
+struct slimpro_resp_msg {
+ u32 msg;
+ u32 param1;
+ u32 param2;
+} __packed;
+
+struct xgene_hwmon_dev {
+ struct device *dev;
+ struct mbox_chan *mbox_chan;
+ struct mbox_client mbox_client;
+ int mbox_idx;
+
+ spinlock_t kfifo_lock;
+ struct mutex rd_mutex;
+ struct completion rd_complete;
+ int resp_pending;
+ struct slimpro_resp_msg sync_msg;
+
+ struct work_struct workq;
+ struct kfifo_rec_ptr_1 async_msg_fifo;
+
+ struct device *hwmon_dev;
+ bool temp_critical_alarm;
+
+ phys_addr_t comm_base_addr;
+ void *pcc_comm_addr;
+ u64 usecs_lat;
+};
+
+/*
+ * This function tests and clears a bitmask then returns its old value
+ */
+static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
+{
+ u16 ret, val;
+
+ val = le16_to_cpu(READ_ONCE(*addr));
+ ret = val & mask;
+ val &= ~mask;
+ WRITE_ONCE(*addr, cpu_to_le16(val));
+
+ return ret;
+}
+
+static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
+{
+ struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ u32 *ptr = (void *)(generic_comm_base + 1);
+ int rc, i;
+ u16 val;
+
+ mutex_lock(&ctx->rd_mutex);
+ init_completion(&ctx->rd_complete);
+ ctx->resp_pending = true;
+
+ /* Write signature for subspace */
+ WRITE_ONCE(generic_comm_base->signature,
+ cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx));
+
+ /* Write to the shared command region */
+ WRITE_ONCE(generic_comm_base->command,
+ cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT));
+
+ /* Flip CMD COMPLETE bit */
+ val = le16_to_cpu(READ_ONCE(generic_comm_base->status));
+ val &= ~PCCS_CMD_COMPLETE;
+ WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val));
+
+ /* Copy the message to the PCC comm space */
+ for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++)
+ WRITE_ONCE(ptr[i], cpu_to_le32(msg[i]));
+
+ /* Ring the doorbell */
+ rc = mbox_send_message(ctx->mbox_chan, msg);
+ if (rc < 0) {
+ dev_err(ctx->dev, "Mailbox send error %d\n", rc);
+ goto err;
+ }
+ if (!wait_for_completion_timeout(&ctx->rd_complete,
+ usecs_to_jiffies(ctx->usecs_lat))) {
+ dev_err(ctx->dev, "Mailbox operation timed out\n");
+ rc = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* Check for error message */
+ if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ msg[0] = ctx->sync_msg.msg;
+ msg[1] = ctx->sync_msg.param1;
+ msg[2] = ctx->sync_msg.param2;
+
+err:
+ mbox_chan_txdone(ctx->mbox_chan, 0);
+ ctx->resp_pending = false;
+ mutex_unlock(&ctx->rd_mutex);
+ return rc;
+}
+
+static int xgene_hwmon_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
+{
+ int rc;
+
+ mutex_lock(&ctx->rd_mutex);
+ init_completion(&ctx->rd_complete);
+ ctx->resp_pending = true;
+
+ rc = mbox_send_message(ctx->mbox_chan, msg);
+ if (rc < 0) {
+ dev_err(ctx->dev, "Mailbox send error %d\n", rc);
+ goto err;
+ }
+
+ if (!wait_for_completion_timeout(&ctx->rd_complete,
+ msecs_to_jiffies(MBOX_OP_TIMEOUTMS))) {
+ dev_err(ctx->dev, "Mailbox operation timed out\n");
+ rc = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* Check for error message */
+ if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ msg[0] = ctx->sync_msg.msg;
+ msg[1] = ctx->sync_msg.param1;
+ msg[2] = ctx->sync_msg.param2;
+
+err:
+ ctx->resp_pending = false;
+ mutex_unlock(&ctx->rd_mutex);
+ return rc;
+}
+
+static int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr,
+ u32 *data)
+{
+ u32 msg[3];
+ int rc;
+
+ msg[0] = SENSOR_RD_MSG;
+ msg[1] = SENSOR_RD_EN_ADDR(addr);
+ msg[2] = 0;
+
+ if (acpi_disabled)
+ rc = xgene_hwmon_rd(ctx, msg);
+ else
+ rc = xgene_hwmon_pcc_rd(ctx, msg);
+
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Check if sensor data is valid.
+ */
+ if (msg[1] & SENSOR_INVALID_DATA)
+ return -ENODATA;
+
+ *data = msg[1];
+
+ return rc;
+}
+
+static int xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev *ctx,
+ u32 *amsg)
+{
+ u32 msg[3];
+ int rc;
+
+ msg[0] = TPC_EN_MSG(PWRMGMT_SUBTYPE_TPC, TPC_GET_ALARM, 0);
+ msg[1] = 0;
+ msg[2] = 0;
+
+ rc = xgene_hwmon_pcc_rd(ctx, msg);
+ if (rc < 0)
+ return rc;
+
+ amsg[0] = msg[0];
+ amsg[1] = msg[1];
+ amsg[2] = msg[2];
+
+ return rc;
+}
+
+static int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ u32 watt, mwatt;
+ int rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt);
+ if (rc < 0)
+ return rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt);
+ if (rc < 0)
+ return rc;
+
+ *val = WATT_TO_mWATT(watt) + mwatt;
+ return 0;
+}
+
+static int xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ u32 watt, mwatt;
+ int rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_REG, &watt);
+ if (rc < 0)
+ return rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_MW_REG, &mwatt);
+ if (rc < 0)
+ return rc;
+
+ *val = WATT_TO_mWATT(watt) + mwatt;
+ return 0;
+}
+
+static int xgene_hwmon_get_temp(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ return xgene_hwmon_reg_map_rd(ctx, SOC_TEMP_REG, val);
+}
+
+/*
+ * Sensor temperature/power functions
+ */
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ int rc, temp;
+ u32 val;
+
+ rc = xgene_hwmon_get_temp(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ temp = sign_extend32(val, TEMP_NEGATIVE_BIT);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp));
+}
+
+static ssize_t temp1_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "SoC Temperature\n");
+}
+
+static ssize_t temp1_critical_alarm_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm);
+}
+
+static ssize_t power1_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "CPU power\n");
+}
+
+static ssize_t power2_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "IO power\n");
+}
+
+static ssize_t power1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ u32 val;
+ int rc;
+
+ rc = xgene_hwmon_get_cpu_pwr(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
+}
+
+static ssize_t power2_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ u32 val;
+ int rc;
+
+ rc = xgene_hwmon_get_io_pwr(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
+}
+
+static DEVICE_ATTR_RO(temp1_label);
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RO(temp1_critical_alarm);
+static DEVICE_ATTR_RO(power1_label);
+static DEVICE_ATTR_RO(power1_input);
+static DEVICE_ATTR_RO(power2_label);
+static DEVICE_ATTR_RO(power2_input);
+
+static struct attribute *xgene_hwmon_attrs[] = {
+ &dev_attr_temp1_label.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_critical_alarm.attr,
+ &dev_attr_power1_label.attr,
+ &dev_attr_power1_input.attr,
+ &dev_attr_power2_label.attr,
+ &dev_attr_power2_input.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(xgene_hwmon);
+
+static int xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev *ctx,
+ struct slimpro_resp_msg *amsg)
+{
+ ctx->temp_critical_alarm = !!amsg->param2;
+ sysfs_notify(&ctx->dev->kobj, NULL, "temp1_critical_alarm");
+
+ return 0;
+}
+
+static void xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev *ctx,
+ struct slimpro_resp_msg *amsg)
+{
+ if ((MSG_SUBTYPE(amsg->msg) == PWRMGMT_SUBTYPE_TPC) &&
+ (TPC_CMD(amsg->msg) == TPC_ALARM))
+ xgene_hwmon_tpc_alarm(ctx, amsg);
+}
+
+/*
+ * This function is called to process async work queue
+ */
+static void xgene_hwmon_evt_work(struct work_struct *work)
+{
+ struct slimpro_resp_msg amsg;
+ struct xgene_hwmon_dev *ctx;
+ int ret;
+
+ ctx = container_of(work, struct xgene_hwmon_dev, workq);
+ while (kfifo_out_spinlocked(&ctx->async_msg_fifo, &amsg,
+ sizeof(struct slimpro_resp_msg),
+ &ctx->kfifo_lock)) {
+ /*
+ * If PCC, send a consumer command to Platform to get info
+ * If Slimpro Mailbox, get message from specific FIFO
+ */
+ if (!acpi_disabled) {
+ ret = xgene_hwmon_get_notification_msg(ctx,
+ (u32 *)&amsg);
+ if (ret < 0)
+ continue;
+ }
+
+ if (MSG_TYPE(amsg.msg) == MSG_TYPE_PWRMGMT)
+ xgene_hwmon_process_pwrmsg(ctx, &amsg);
+ }
+}
+
+static int xgene_hwmon_rx_ready(struct xgene_hwmon_dev *ctx, void *msg)
+{
+ if (IS_ERR_OR_NULL(ctx->hwmon_dev) && !ctx->resp_pending) {
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
+ sizeof(struct slimpro_resp_msg),
+ &ctx->kfifo_lock);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called when the SLIMpro Mailbox received a message
+ */
+static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg)
+{
+ struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
+
+ /*
+ * While the driver registers with the mailbox framework, an interrupt
+ * can be pending before the probe function completes its
+ * initialization. If such condition occurs, just queue up the message
+ * as the driver is not ready for servicing the callback.
+ */
+ if (xgene_hwmon_rx_ready(ctx, msg) < 0)
+ return;
+
+ /*
+ * Response message format:
+ * msg[0] is the return code of the operation
+ * msg[1] is the first parameter word
+ * msg[2] is the second parameter word
+ *
+ * As message only supports dword size, just assign it.
+ */
+
+ /* Check for sync query */
+ if (ctx->resp_pending &&
+ ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
+ TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
+ ctx->sync_msg.msg = ((u32 *)msg)[0];
+ ctx->sync_msg.param1 = ((u32 *)msg)[1];
+ ctx->sync_msg.param2 = ((u32 *)msg)[2];
+
+ /* Operation waiting for response */
+ complete(&ctx->rd_complete);
+
+ return;
+ }
+
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
+ sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
+ /* Schedule the bottom handler */
+ schedule_work(&ctx->workq);
+}
+
+/*
+ * This function is called when the PCC Mailbox received a message
+ */
+static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)
+{
+ struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
+ struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ struct slimpro_resp_msg amsg;
+
+ /*
+ * While the driver registers with the mailbox framework, an interrupt
+ * can be pending before the probe function completes its
+ * initialization. If such condition occurs, just queue up the message
+ * as the driver is not ready for servicing the callback.
+ */
+ if (xgene_hwmon_rx_ready(ctx, &amsg) < 0)
+ return;
+
+ msg = generic_comm_base + 1;
+ /* Check if platform sends interrupt */
+ if (!xgene_word_tst_and_clr(&generic_comm_base->status,
+ PCCS_SCI_DOORBEL))
+ return;
+
+ /*
+ * Response message format:
+ * msg[0] is the return code of the operation
+ * msg[1] is the first parameter word
+ * msg[2] is the second parameter word
+ *
+ * As message only supports dword size, just assign it.
+ */
+
+ /* Check for sync query */
+ if (ctx->resp_pending &&
+ ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
+ TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
+ /* Check if platform completes command */
+ if (xgene_word_tst_and_clr(&generic_comm_base->status,
+ PCCS_CMD_COMPLETE)) {
+ ctx->sync_msg.msg = ((u32 *)msg)[0];
+ ctx->sync_msg.param1 = ((u32 *)msg)[1];
+ ctx->sync_msg.param2 = ((u32 *)msg)[2];
+
+ /* Operation waiting for response */
+ complete(&ctx->rd_complete);
+
+ return;
+ }
+ }
+
+ /*
+ * Platform notifies interrupt to OSPM.
+ * OPSM schedules a consumer command to get this information
+ * in a workqueue. Platform must wait until OSPM has issued
+ * a consumer command that serves this notification.
+ */
+
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg,
+ sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
+ /* Schedule the bottom handler */
+ schedule_work(&ctx->workq);
+}
+
+static void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+ if (ret) {
+ dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n",
+ *(u16 *)msg, ret);
+ } else {
+ dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n",
+ *(u16 *)msg, ret);
+ }
+}
+
+static int xgene_hwmon_probe(struct platform_device *pdev)
+{
+ struct xgene_hwmon_dev *ctx;
+ struct mbox_client *cl;
+ int rc;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ctx);
+ cl = &ctx->mbox_client;
+
+ spin_lock_init(&ctx->kfifo_lock);
+ mutex_init(&ctx->rd_mutex);
+
+ rc = kfifo_alloc(&ctx->async_msg_fifo,
+ sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE,
+ GFP_KERNEL);
+ if (rc)
+ goto out_mbox_free;
+
+ INIT_WORK(&ctx->workq, xgene_hwmon_evt_work);
+
+ /* Request mailbox channel */
+ cl->dev = &pdev->dev;
+ cl->tx_done = xgene_hwmon_tx_done;
+ cl->tx_block = false;
+ cl->tx_tout = MBOX_OP_TIMEOUTMS;
+ cl->knows_txdone = false;
+ if (acpi_disabled) {
+ cl->rx_callback = xgene_hwmon_rx_cb;
+ ctx->mbox_chan = mbox_request_channel(cl, 0);
+ if (IS_ERR(ctx->mbox_chan)) {
+ dev_err(&pdev->dev,
+ "SLIMpro mailbox channel request failed\n");
+ return -ENODEV;
+ }
+ } else {
+ struct acpi_pcct_hw_reduced *cppc_ss;
+
+ if (device_property_read_u32(&pdev->dev, "pcc-channel",
+ &ctx->mbox_idx)) {
+ dev_err(&pdev->dev, "no pcc-channel property\n");
+ return -ENODEV;
+ }
+
+ cl->rx_callback = xgene_hwmon_pcc_rx_cb;
+ ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
+ if (IS_ERR(ctx->mbox_chan)) {
+ dev_err(&pdev->dev,
+ "PPC channel request failed\n");
+ return -ENODEV;
+ }
+
+ /*
+ * The PCC mailbox controller driver should
+ * have parsed the PCCT (global table of all
+ * PCC channels) and stored pointers to the
+ * subspace communication region in con_priv.
+ */
+ cppc_ss = ctx->mbox_chan->con_priv;
+ if (!cppc_ss) {
+ dev_err(&pdev->dev, "PPC subspace not found\n");
+ rc = -ENODEV;
+ goto out_mbox_free;
+ }
+
+ if (!ctx->mbox_chan->mbox->txdone_irq) {
+ dev_err(&pdev->dev, "PCC IRQ not supported\n");
+ rc = -ENODEV;
+ goto out_mbox_free;
+ }
+
+ /*
+ * This is the shared communication region
+ * for the OS and Platform to communicate over.
+ */
+ ctx->comm_base_addr = cppc_ss->base_address;
+ if (ctx->comm_base_addr) {
+ ctx->pcc_comm_addr = memremap(ctx->comm_base_addr,
+ cppc_ss->length,
+ MEMREMAP_WB);
+ } else {
+ dev_err(&pdev->dev, "Failed to get PCC comm region\n");
+ rc = -ENODEV;
+ goto out_mbox_free;
+ }
+
+ if (!ctx->pcc_comm_addr) {
+ dev_err(&pdev->dev,
+ "Failed to ioremap PCC comm region\n");
+ rc = -ENOMEM;
+ goto out_mbox_free;
+ }
+
+ /*
+ * cppc_ss->latency is just a Nominal value. In reality
+ * the remote processor could be much slower to reply.
+ * So add an arbitrary amount of wait on top of Nominal.
+ */
+ ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency;
+ }
+
+ ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
+ "apm_xgene",
+ ctx,
+ xgene_hwmon_groups);
+ if (IS_ERR(ctx->hwmon_dev)) {
+ dev_err(&pdev->dev, "Failed to register HW monitor device\n");
+ rc = PTR_ERR(ctx->hwmon_dev);
+ goto out;
+ }
+
+ /*
+ * Schedule the bottom handler if there is a pending message.
+ */
+ schedule_work(&ctx->workq);
+
+ dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n");
+
+ return 0;
+
+out:
+ if (acpi_disabled)
+ mbox_free_channel(ctx->mbox_chan);
+ else
+ pcc_mbox_free_channel(ctx->mbox_chan);
+out_mbox_free:
+ kfifo_free(&ctx->async_msg_fifo);
+
+ return rc;
+}
+
+static int xgene_hwmon_remove(struct platform_device *pdev)
+{
+ struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(ctx->hwmon_dev);
+ kfifo_free(&ctx->async_msg_fifo);
+ if (acpi_disabled)
+ mbox_free_channel(ctx->mbox_chan);
+ else
+ pcc_mbox_free_channel(ctx->mbox_chan);
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_hwmon_acpi_match[] = {
+ {"APMC0D29", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match);
+#endif
+
+static const struct of_device_id xgene_hwmon_of_match[] = {
+ {.compatible = "apm,xgene-slimpro-hwmon"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
+
+static struct platform_driver xgene_hwmon_driver __refdata = {
+ .probe = xgene_hwmon_probe,
+ .remove = xgene_hwmon_remove,
+ .driver = {
+ .name = "xgene-slimpro-hwmon",
+ .of_match_table = xgene_hwmon_of_match,
+ .acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match),
+ },
+};
+module_platform_driver(xgene_hwmon_driver);
+
+MODULE_DESCRIPTION("APM X-Gene SoC hardware monitor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
index 4d20b0b..d7325c65 100644
--- a/drivers/hwtracing/coresight/coresight-etb10.c
+++ b/drivers/hwtracing/coresight/coresight-etb10.c
@@ -184,8 +184,7 @@ static void etb_disable_hw(struct etb_drvdata *drvdata)
if (coresight_timeout(drvdata->base, ETB_FFCR, ETB_FFCR_BIT, 0)) {
dev_err(drvdata->dev,
- "timeout observed when probing at offset %#x\n",
- ETB_FFCR);
+ "timeout while waiting for completion of Manual Flush\n");
}
/* disable trace capture */
@@ -193,8 +192,7 @@ static void etb_disable_hw(struct etb_drvdata *drvdata)
if (coresight_timeout(drvdata->base, ETB_FFSR, ETB_FFSR_BIT, 1)) {
dev_err(drvdata->dev,
- "timeout observed when probing at offset %#x\n",
- ETB_FFCR);
+ "timeout while waiting for Formatter to Stop\n");
}
CS_LOCK(drvdata->base);
@@ -561,7 +559,7 @@ static const struct file_operations etb_fops = {
};
#define coresight_etb10_simple_func(name, offset) \
- coresight_simple_func(struct etb_drvdata, name, offset)
+ coresight_simple_func(struct etb_drvdata, NULL, name, offset)
coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG);
coresight_etb10_simple_func(sts, ETB_STATUS_REG);
@@ -638,7 +636,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL;
struct etb_drvdata *drvdata;
struct resource *res = &adev->res;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
if (np) {
@@ -684,17 +682,13 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
return -ENOMEM;
}
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
- desc->type = CORESIGHT_DEV_TYPE_SINK;
- desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
- desc->ops = &etb_cs_ops;
- desc->pdata = pdata;
- desc->dev = dev;
- desc->groups = coresight_etb_groups;
- drvdata->csdev = coresight_register(desc);
+ desc.type = CORESIGHT_DEV_TYPE_SINK;
+ desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
+ desc.ops = &etb_cs_ops;
+ desc.pdata = pdata;
+ desc.dev = dev;
+ desc.groups = coresight_etb_groups;
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 755125f..2cd7c71 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -27,6 +27,7 @@
#include <linux/types.h>
#include <linux/workqueue.h>
+#include "coresight-etm-perf.h"
#include "coresight-priv.h"
static struct pmu etm_pmu;
@@ -71,14 +72,48 @@ static const struct attribute_group *etm_pmu_attr_groups[] = {
static void etm_event_read(struct perf_event *event) {}
-static int etm_event_init(struct perf_event *event)
+static int etm_addr_filters_alloc(struct perf_event *event)
{
- if (event->attr.type != etm_pmu.type)
- return -ENOENT;
+ struct etm_filters *filters;
+ int node = event->cpu == -1 ? -1 : cpu_to_node(event->cpu);
+
+ filters = kzalloc_node(sizeof(struct etm_filters), GFP_KERNEL, node);
+ if (!filters)
+ return -ENOMEM;
+
+ if (event->parent)
+ memcpy(filters, event->parent->hw.addr_filters,
+ sizeof(*filters));
+
+ event->hw.addr_filters = filters;
return 0;
}
+static void etm_event_destroy(struct perf_event *event)
+{
+ kfree(event->hw.addr_filters);
+ event->hw.addr_filters = NULL;
+}
+
+static int etm_event_init(struct perf_event *event)
+{
+ int ret = 0;
+
+ if (event->attr.type != etm_pmu.type) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = etm_addr_filters_alloc(event);
+ if (ret)
+ goto out;
+
+ event->destroy = etm_event_destroy;
+out:
+ return ret;
+}
+
static void free_event_data(struct work_struct *work)
{
int cpu;
@@ -100,7 +135,7 @@ static void free_event_data(struct work_struct *work)
}
for_each_cpu(cpu, mask) {
- if (event_data->path[cpu])
+ if (!(IS_ERR_OR_NULL(event_data->path[cpu])))
coresight_release_path(event_data->path[cpu]);
}
@@ -185,7 +220,7 @@ static void *etm_setup_aux(int event_cpu, void **pages,
* referenced later when the path is actually needed.
*/
event_data->path[cpu] = coresight_build_path(csdev);
- if (!event_data->path[cpu])
+ if (IS_ERR(event_data->path[cpu]))
goto err;
}
@@ -258,7 +293,7 @@ static void etm_event_start(struct perf_event *event, int flags)
event->hw.state = 0;
/* Finally enable the tracer */
- if (source_ops(csdev)->enable(csdev, &event->attr, CS_MODE_PERF))
+ if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF))
goto fail_end_stop;
out:
@@ -291,7 +326,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
return;
/* stop tracer */
- source_ops(csdev)->disable(csdev);
+ source_ops(csdev)->disable(csdev, event);
/* tell the core */
event->hw.state = PERF_HES_STOPPED;
@@ -342,6 +377,87 @@ static void etm_event_del(struct perf_event *event, int mode)
etm_event_stop(event, PERF_EF_UPDATE);
}
+static int etm_addr_filters_validate(struct list_head *filters)
+{
+ bool range = false, address = false;
+ int index = 0;
+ struct perf_addr_filter *filter;
+
+ list_for_each_entry(filter, filters, entry) {
+ /*
+ * No need to go further if there's no more
+ * room for filters.
+ */
+ if (++index > ETM_ADDR_CMP_MAX)
+ return -EOPNOTSUPP;
+
+ /*
+ * As taken from the struct perf_addr_filter documentation:
+ * @range: 1: range, 0: address
+ *
+ * At this time we don't allow range and start/stop filtering
+ * to cohabitate, they have to be mutually exclusive.
+ */
+ if ((filter->range == 1) && address)
+ return -EOPNOTSUPP;
+
+ if ((filter->range == 0) && range)
+ return -EOPNOTSUPP;
+
+ /*
+ * For range filtering, the second address in the address
+ * range comparator needs to be higher than the first.
+ * Invalid otherwise.
+ */
+ if (filter->range && filter->size == 0)
+ return -EINVAL;
+
+ /*
+ * Everything checks out with this filter, record what we've
+ * received before moving on to the next one.
+ */
+ if (filter->range)
+ range = true;
+ else
+ address = true;
+ }
+
+ return 0;
+}
+
+static void etm_addr_filters_sync(struct perf_event *event)
+{
+ struct perf_addr_filters_head *head = perf_event_addr_filters(event);
+ unsigned long start, stop, *offs = event->addr_filters_offs;
+ struct etm_filters *filters = event->hw.addr_filters;
+ struct etm_filter *etm_filter;
+ struct perf_addr_filter *filter;
+ int i = 0;
+
+ list_for_each_entry(filter, &head->list, entry) {
+ start = filter->offset + offs[i];
+ stop = start + filter->size;
+ etm_filter = &filters->etm_filter[i];
+
+ if (filter->range == 1) {
+ etm_filter->start_addr = start;
+ etm_filter->stop_addr = stop;
+ etm_filter->type = ETM_ADDR_TYPE_RANGE;
+ } else {
+ if (filter->filter == 1) {
+ etm_filter->start_addr = start;
+ etm_filter->type = ETM_ADDR_TYPE_START;
+ } else {
+ etm_filter->stop_addr = stop;
+ etm_filter->type = ETM_ADDR_TYPE_STOP;
+ }
+ }
+ i++;
+ }
+
+ filters->nr_filters = i;
+}
+
int etm_perf_symlink(struct coresight_device *csdev, bool link)
{
char entry[sizeof("cpu9999999")];
@@ -371,18 +487,21 @@ static int __init etm_perf_init(void)
{
int ret;
- etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE;
-
- etm_pmu.attr_groups = etm_pmu_attr_groups;
- etm_pmu.task_ctx_nr = perf_sw_context;
- etm_pmu.read = etm_event_read;
- etm_pmu.event_init = etm_event_init;
- etm_pmu.setup_aux = etm_setup_aux;
- etm_pmu.free_aux = etm_free_aux;
- etm_pmu.start = etm_event_start;
- etm_pmu.stop = etm_event_stop;
- etm_pmu.add = etm_event_add;
- etm_pmu.del = etm_event_del;
+ etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE;
+
+ etm_pmu.attr_groups = etm_pmu_attr_groups;
+ etm_pmu.task_ctx_nr = perf_sw_context;
+ etm_pmu.read = etm_event_read;
+ etm_pmu.event_init = etm_event_init;
+ etm_pmu.setup_aux = etm_setup_aux;
+ etm_pmu.free_aux = etm_free_aux;
+ etm_pmu.start = etm_event_start;
+ etm_pmu.stop = etm_event_stop;
+ etm_pmu.add = etm_event_add;
+ etm_pmu.del = etm_event_del;
+ etm_pmu.addr_filters_sync = etm_addr_filters_sync;
+ etm_pmu.addr_filters_validate = etm_addr_filters_validate;
+ etm_pmu.nr_addr_filters = ETM_ADDR_CMP_MAX;
ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
if (ret == 0)
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
index 87f5a13..3ffc9fe 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.h
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -18,8 +18,42 @@
#ifndef _CORESIGHT_ETM_PERF_H
#define _CORESIGHT_ETM_PERF_H
+#include "coresight-priv.h"
+
struct coresight_device;
+/*
+ * In both ETMv3 and v4 the maximum number of address comparator implentable
+ * is 8. The actual number is implementation specific and will be checked
+ * when filters are applied.
+ */
+#define ETM_ADDR_CMP_MAX 8
+
+/**
+ * struct etm_filter - single instruction range or start/stop configuration.
+ * @start_addr: The address to start tracing on.
+ * @stop_addr: The address to stop tracing on.
+ * @type: Is this a range or start/stop filter.
+ */
+struct etm_filter {
+ unsigned long start_addr;
+ unsigned long stop_addr;
+ enum etm_addr_type type;
+};
+
+/**
+ * struct etm_filters - set of filters for a session
+ * @etm_filter: All the filters for this session.
+ * @nr_filters: Number of filters
+ * @ssstatus: Status of the start/stop logic.
+ */
+struct etm_filters {
+ struct etm_filter etm_filter[ETM_ADDR_CMP_MAX];
+ unsigned int nr_filters;
+ bool ssstatus;
+};
+
+
#ifdef CONFIG_CORESIGHT
int etm_perf_symlink(struct coresight_device *csdev, bool link);
diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
index 51597cb..4a18ee4 100644
--- a/drivers/hwtracing/coresight/coresight-etm.h
+++ b/drivers/hwtracing/coresight/coresight-etm.h
@@ -259,14 +259,6 @@ struct etm_drvdata {
struct etm_config config;
};
-enum etm_addr_type {
- ETM_ADDR_TYPE_NONE,
- ETM_ADDR_TYPE_SINGLE,
- ETM_ADDR_TYPE_RANGE,
- ETM_ADDR_TYPE_START,
- ETM_ADDR_TYPE_STOP,
-};
-
static inline void etm_writel(struct etm_drvdata *drvdata,
u32 val, u32 off)
{
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
index 02d4b62..e9b0719 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c
@@ -18,6 +18,7 @@
#include <linux/pm_runtime.h>
#include <linux/sysfs.h>
#include "coresight-etm.h"
+#include "coresight-priv.h"
static ssize_t nr_addr_cmp_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -1222,7 +1223,7 @@ static struct attribute *coresight_etm_attrs[] = {
};
#define coresight_etm3x_simple_func(name, offset) \
- coresight_simple_func(struct etm_drvdata, name, offset)
+ coresight_simple_func(struct etm_drvdata, NULL, name, offset)
coresight_etm3x_simple_func(etmccr, ETMCCR);
coresight_etm3x_simple_func(etmccer, ETMCCER);
diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
index 2de4cad..3fe368b 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x.c
@@ -311,9 +311,10 @@ void etm_config_trace_mode(struct etm_config *config)
#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN)
static int etm_parse_event_config(struct etm_drvdata *drvdata,
- struct perf_event_attr *attr)
+ struct perf_event *event)
{
struct etm_config *config = &drvdata->config;
+ struct perf_event_attr *attr = &event->attr;
if (!attr)
return -EINVAL;
@@ -459,7 +460,7 @@ static int etm_trace_id(struct coresight_device *csdev)
}
static int etm_enable_perf(struct coresight_device *csdev,
- struct perf_event_attr *attr)
+ struct perf_event *event)
{
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -467,7 +468,7 @@ static int etm_enable_perf(struct coresight_device *csdev,
return -EINVAL;
/* Configure the tracer based on the session's specifics */
- etm_parse_event_config(drvdata, attr);
+ etm_parse_event_config(drvdata, event);
/* And enable it */
etm_enable_hw(drvdata);
@@ -504,7 +505,7 @@ err:
}
static int etm_enable(struct coresight_device *csdev,
- struct perf_event_attr *attr, u32 mode)
+ struct perf_event *event, u32 mode)
{
int ret;
u32 val;
@@ -521,7 +522,7 @@ static int etm_enable(struct coresight_device *csdev,
ret = etm_enable_sysfs(csdev);
break;
case CS_MODE_PERF:
- ret = etm_enable_perf(csdev, attr);
+ ret = etm_enable_perf(csdev, event);
break;
default:
ret = -EINVAL;
@@ -601,7 +602,8 @@ static void etm_disable_sysfs(struct coresight_device *csdev)
dev_info(drvdata->dev, "ETM tracing disabled\n");
}
-static void etm_disable(struct coresight_device *csdev)
+static void etm_disable(struct coresight_device *csdev,
+ struct perf_event *event)
{
u32 mode;
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -756,13 +758,9 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL;
struct etm_drvdata *drvdata;
struct resource *res = &adev->res;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
@@ -825,13 +823,13 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
etm_init_trace_id(drvdata);
etm_set_default(&drvdata->config);
- desc->type = CORESIGHT_DEV_TYPE_SOURCE;
- desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
- desc->ops = &etm_cs_ops;
- desc->pdata = pdata;
- desc->dev = dev;
- desc->groups = coresight_etm_groups;
- drvdata->csdev = coresight_register(desc);
+ desc.type = CORESIGHT_DEV_TYPE_SOURCE;
+ desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
+ desc.ops = &etm_cs_ops;
+ desc.pdata = pdata;
+ desc.dev = dev;
+ desc.groups = coresight_etm_groups;
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto err_arch_supported;
@@ -893,6 +891,11 @@ static struct amba_id etm_ids[] = {
.mask = 0x0003ffff,
.data = "ETM 3.3",
},
+ { /* ETM 3.5 - Cortex-A5 */
+ .id = 0x0003b955,
+ .mask = 0x0003ffff,
+ .data = "ETM 3.5",
+ },
{ /* ETM 3.5 */
.id = 0x0003b956,
.mask = 0x0003ffff,
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
index 7c84308..b9b1e9c 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c
@@ -18,6 +18,7 @@
#include <linux/pm_runtime.h>
#include <linux/sysfs.h>
#include "coresight-etm4x.h"
+#include "coresight-priv.h"
static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude)
{
@@ -2039,15 +2040,42 @@ static struct attribute *coresight_etmv4_attrs[] = {
NULL,
};
+struct etmv4_reg {
+ void __iomem *addr;
+ u32 data;
+};
+
+static void do_smp_cross_read(void *data)
+{
+ struct etmv4_reg *reg = data;
+
+ reg->data = readl_relaxed(reg->addr);
+}
+
+static u32 etmv4_cross_read(const struct device *dev, u32 offset)
+{
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(dev);
+ struct etmv4_reg reg;
+
+ reg.addr = drvdata->base + offset;
+ /*
+ * smp cross call ensures the CPU will be powered up before
+ * accessing the ETMv4 trace core registers
+ */
+ smp_call_function_single(drvdata->cpu, do_smp_cross_read, &reg, 1);
+ return reg.data;
+}
+
#define coresight_etm4x_simple_func(name, offset) \
- coresight_simple_func(struct etmv4_drvdata, name, offset)
+ coresight_simple_func(struct etmv4_drvdata, NULL, name, offset)
+
+#define coresight_etm4x_cross_read(name, offset) \
+ coresight_simple_func(struct etmv4_drvdata, etmv4_cross_read, \
+ name, offset)
-coresight_etm4x_simple_func(trcoslsr, TRCOSLSR);
coresight_etm4x_simple_func(trcpdcr, TRCPDCR);
coresight_etm4x_simple_func(trcpdsr, TRCPDSR);
coresight_etm4x_simple_func(trclsr, TRCLSR);
-coresight_etm4x_simple_func(trcconfig, TRCCONFIGR);
-coresight_etm4x_simple_func(trctraceid, TRCTRACEIDR);
coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS);
coresight_etm4x_simple_func(trcdevid, TRCDEVID);
coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE);
@@ -2055,6 +2083,9 @@ coresight_etm4x_simple_func(trcpidr0, TRCPIDR0);
coresight_etm4x_simple_func(trcpidr1, TRCPIDR1);
coresight_etm4x_simple_func(trcpidr2, TRCPIDR2);
coresight_etm4x_simple_func(trcpidr3, TRCPIDR3);
+coresight_etm4x_cross_read(trcoslsr, TRCOSLSR);
+coresight_etm4x_cross_read(trcconfig, TRCCONFIGR);
+coresight_etm4x_cross_read(trctraceid, TRCTRACEIDR);
static struct attribute *coresight_etmv4_mgmt_attrs[] = {
&dev_attr_trcoslsr.attr,
@@ -2073,19 +2104,19 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = {
NULL,
};
-coresight_etm4x_simple_func(trcidr0, TRCIDR0);
-coresight_etm4x_simple_func(trcidr1, TRCIDR1);
-coresight_etm4x_simple_func(trcidr2, TRCIDR2);
-coresight_etm4x_simple_func(trcidr3, TRCIDR3);
-coresight_etm4x_simple_func(trcidr4, TRCIDR4);
-coresight_etm4x_simple_func(trcidr5, TRCIDR5);
+coresight_etm4x_cross_read(trcidr0, TRCIDR0);
+coresight_etm4x_cross_read(trcidr1, TRCIDR1);
+coresight_etm4x_cross_read(trcidr2, TRCIDR2);
+coresight_etm4x_cross_read(trcidr3, TRCIDR3);
+coresight_etm4x_cross_read(trcidr4, TRCIDR4);
+coresight_etm4x_cross_read(trcidr5, TRCIDR5);
/* trcidr[6,7] are reserved */
-coresight_etm4x_simple_func(trcidr8, TRCIDR8);
-coresight_etm4x_simple_func(trcidr9, TRCIDR9);
-coresight_etm4x_simple_func(trcidr10, TRCIDR10);
-coresight_etm4x_simple_func(trcidr11, TRCIDR11);
-coresight_etm4x_simple_func(trcidr12, TRCIDR12);
-coresight_etm4x_simple_func(trcidr13, TRCIDR13);
+coresight_etm4x_cross_read(trcidr8, TRCIDR8);
+coresight_etm4x_cross_read(trcidr9, TRCIDR9);
+coresight_etm4x_cross_read(trcidr10, TRCIDR10);
+coresight_etm4x_cross_read(trcidr11, TRCIDR11);
+coresight_etm4x_cross_read(trcidr12, TRCIDR12);
+coresight_etm4x_cross_read(trcidr13, TRCIDR13);
static struct attribute *coresight_etmv4_trcidr_attrs[] = {
&dev_attr_trcidr0.attr,
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 1a5e0d1..4db8d6a 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -33,7 +33,6 @@
#include <linux/uaccess.h>
#include <linux/perf_event.h>
#include <linux/pm_runtime.h>
-#include <linux/perf_event.h>
#include <asm/sections.h>
#include <asm/local.h>
@@ -46,7 +45,9 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO);
/* The number of ETMv4 currently registered */
static int etm4_count;
static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
-static void etm4_set_default(struct etmv4_config *config);
+static void etm4_set_default_config(struct etmv4_config *config);
+static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
+ struct perf_event *event);
static enum cpuhp_state hp_online;
@@ -79,22 +80,8 @@ static int etm4_cpu_id(struct coresight_device *csdev)
static int etm4_trace_id(struct coresight_device *csdev)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- unsigned long flags;
- int trace_id = -1;
-
- if (!local_read(&drvdata->mode))
- return drvdata->trcid;
-
- spin_lock_irqsave(&drvdata->spinlock, flags);
-
- CS_UNLOCK(drvdata->base);
- trace_id = readl_relaxed(drvdata->base + TRCTRACEIDR);
- trace_id &= ETM_TRACEID_MASK;
- CS_LOCK(drvdata->base);
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
-
- return trace_id;
+ return drvdata->trcid;
}
static void etm4_enable_hw(void *info)
@@ -113,8 +100,7 @@ static void etm4_enable_hw(void *info)
/* wait for TRCSTATR.IDLE to go up */
if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1))
dev_err(drvdata->dev,
- "timeout observed when probing at offset %#x\n",
- TRCSTATR);
+ "timeout while waiting for Idle Trace Status\n");
writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR);
writel_relaxed(config->cfg, drvdata->base + TRCCONFIGR);
@@ -180,14 +166,20 @@ static void etm4_enable_hw(void *info)
writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0);
writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1);
+ /*
+ * Request to keep the trace unit powered and also
+ * emulation of powerdown
+ */
+ writel_relaxed(readl_relaxed(drvdata->base + TRCPDCR) | TRCPDCR_PU,
+ drvdata->base + TRCPDCR);
+
/* Enable the trace unit */
writel_relaxed(1, drvdata->base + TRCPRGCTLR);
/* wait for TRCSTATR.IDLE to go back down to '0' */
if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 0))
dev_err(drvdata->dev,
- "timeout observed when probing at offset %#x\n",
- TRCSTATR);
+ "timeout while waiting for Idle Trace Status\n");
CS_LOCK(drvdata->base);
@@ -195,12 +187,16 @@ static void etm4_enable_hw(void *info)
}
static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
- struct perf_event_attr *attr)
+ struct perf_event *event)
{
+ int ret = 0;
struct etmv4_config *config = &drvdata->config;
+ struct perf_event_attr *attr = &event->attr;
- if (!attr)
- return -EINVAL;
+ if (!attr) {
+ ret = -EINVAL;
+ goto out;
+ }
/* Clear configuration from previous run */
memset(config, 0, sizeof(struct etmv4_config));
@@ -212,14 +208,12 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
config->mode = ETM_MODE_EXCL_USER;
/* Always start from the default config */
- etm4_set_default(config);
+ etm4_set_default_config(config);
- /*
- * By default the tracers are configured to trace the whole address
- * range. Narrow the field only if requested by user space.
- */
- if (config->mode)
- etm4_config_trace_mode(config);
+ /* Configure filters specified on the perf cmd line, if any. */
+ ret = etm4_set_event_filters(drvdata, event);
+ if (ret)
+ goto out;
/* Go from generic option to ETMv4 specifics */
if (attr->config & BIT(ETM_OPT_CYCACC))
@@ -227,23 +221,30 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
if (attr->config & BIT(ETM_OPT_TS))
config->cfg |= ETMv4_MODE_TIMESTAMP;
- return 0;
+out:
+ return ret;
}
static int etm4_enable_perf(struct coresight_device *csdev,
- struct perf_event_attr *attr)
+ struct perf_event *event)
{
+ int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
- return -EINVAL;
+ if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
+ ret = -EINVAL;
+ goto out;
+ }
/* Configure the tracer based on the session's specifics */
- etm4_parse_event_config(drvdata, attr);
+ ret = etm4_parse_event_config(drvdata, event);
+ if (ret)
+ goto out;
/* And enable it */
etm4_enable_hw(drvdata);
- return 0;
+out:
+ return ret;
}
static int etm4_enable_sysfs(struct coresight_device *csdev)
@@ -274,7 +275,7 @@ err:
}
static int etm4_enable(struct coresight_device *csdev,
- struct perf_event_attr *attr, u32 mode)
+ struct perf_event *event, u32 mode)
{
int ret;
u32 val;
@@ -291,7 +292,7 @@ static int etm4_enable(struct coresight_device *csdev,
ret = etm4_enable_sysfs(csdev);
break;
case CS_MODE_PERF:
- ret = etm4_enable_perf(csdev, attr);
+ ret = etm4_enable_perf(csdev, event);
break;
default:
ret = -EINVAL;
@@ -311,6 +312,11 @@ static void etm4_disable_hw(void *info)
CS_UNLOCK(drvdata->base);
+ /* power can be removed from the trace unit now */
+ control = readl_relaxed(drvdata->base + TRCPDCR);
+ control &= ~TRCPDCR_PU;
+ writel_relaxed(control, drvdata->base + TRCPDCR);
+
control = readl_relaxed(drvdata->base + TRCPRGCTLR);
/* EN, bit[0] Trace unit enable bit */
@@ -326,14 +332,28 @@ static void etm4_disable_hw(void *info)
dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
}
-static int etm4_disable_perf(struct coresight_device *csdev)
+static int etm4_disable_perf(struct coresight_device *csdev,
+ struct perf_event *event)
{
+ u32 control;
+ struct etm_filters *filters = event->hw.addr_filters;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
return -EINVAL;
etm4_disable_hw(drvdata);
+
+ /*
+ * Check if the start/stop logic was active when the unit was stopped.
+ * That way we can re-enable the start/stop logic when the process is
+ * scheduled again. Configuration of the start/stop logic happens in
+ * function etm4_set_event_filters().
+ */
+ control = readl_relaxed(drvdata->base + TRCVICTLR);
+ /* TRCVICTLR::SSSTATUS, bit[9] */
+ filters->ssstatus = (control & BIT(9));
+
return 0;
}
@@ -362,7 +382,8 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
dev_info(drvdata->dev, "ETM tracing disabled\n");
}
-static void etm4_disable(struct coresight_device *csdev)
+static void etm4_disable(struct coresight_device *csdev,
+ struct perf_event *event)
{
u32 mode;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -381,7 +402,7 @@ static void etm4_disable(struct coresight_device *csdev)
etm4_disable_sysfs(csdev);
break;
case CS_MODE_PERF:
- etm4_disable_perf(csdev);
+ etm4_disable_perf(csdev, event);
break;
}
@@ -564,21 +585,8 @@ static void etm4_init_arch_data(void *info)
CS_LOCK(drvdata->base);
}
-static void etm4_set_default(struct etmv4_config *config)
+static void etm4_set_default_config(struct etmv4_config *config)
{
- if (WARN_ON_ONCE(!config))
- return;
-
- /*
- * Make default initialisation trace everything
- *
- * Select the "always true" resource selector on the
- * "Enablign Event" line and configure address range comparator
- * '0' to trace all the possible address range. From there
- * configure the "include/exclude" engine to include address
- * range comparator '0'.
- */
-
/* disable all events tracing */
config->eventctrl0 = 0x0;
config->eventctrl1 = 0x0;
@@ -594,6 +602,108 @@ static void etm4_set_default(struct etmv4_config *config)
/* TRCVICTLR::EVENT = 0x01, select the always on logic */
config->vinst_ctrl |= BIT(0);
+}
+
+static u64 etm4_get_access_type(struct etmv4_config *config)
+{
+ u64 access_type = 0;
+
+ /*
+ * EXLEVEL_NS, bits[15:12]
+ * The Exception levels are:
+ * Bit[12] Exception level 0 - Application
+ * Bit[13] Exception level 1 - OS
+ * Bit[14] Exception level 2 - Hypervisor
+ * Bit[15] Never implemented
+ *
+ * Always stay away from hypervisor mode.
+ */
+ access_type = ETM_EXLEVEL_NS_HYP;
+
+ if (config->mode & ETM_MODE_EXCL_KERN)
+ access_type |= ETM_EXLEVEL_NS_OS;
+
+ if (config->mode & ETM_MODE_EXCL_USER)
+ access_type |= ETM_EXLEVEL_NS_APP;
+
+ /*
+ * EXLEVEL_S, bits[11:8], don't trace anything happening
+ * in secure state.
+ */
+ access_type |= (ETM_EXLEVEL_S_APP |
+ ETM_EXLEVEL_S_OS |
+ ETM_EXLEVEL_S_HYP);
+
+ return access_type;
+}
+
+static void etm4_set_comparator_filter(struct etmv4_config *config,
+ u64 start, u64 stop, int comparator)
+{
+ u64 access_type = etm4_get_access_type(config);
+
+ /* First half of default address comparator */
+ config->addr_val[comparator] = start;
+ config->addr_acc[comparator] = access_type;
+ config->addr_type[comparator] = ETM_ADDR_TYPE_RANGE;
+
+ /* Second half of default address comparator */
+ config->addr_val[comparator + 1] = stop;
+ config->addr_acc[comparator + 1] = access_type;
+ config->addr_type[comparator + 1] = ETM_ADDR_TYPE_RANGE;
+
+ /*
+ * Configure the ViewInst function to include this address range
+ * comparator.
+ *
+ * @comparator is divided by two since it is the index in the
+ * etmv4_config::addr_val array but register TRCVIIECTLR deals with
+ * address range comparator _pairs_.
+ *
+ * Therefore:
+ * index 0 -> compatator pair 0
+ * index 2 -> comparator pair 1
+ * index 4 -> comparator pair 2
+ * ...
+ * index 14 -> comparator pair 7
+ */
+ config->viiectlr |= BIT(comparator / 2);
+}
+
+static void etm4_set_start_stop_filter(struct etmv4_config *config,
+ u64 address, int comparator,
+ enum etm_addr_type type)
+{
+ int shift;
+ u64 access_type = etm4_get_access_type(config);
+
+ /* Configure the comparator */
+ config->addr_val[comparator] = address;
+ config->addr_acc[comparator] = access_type;
+ config->addr_type[comparator] = type;
+
+ /*
+ * Configure ViewInst Start-Stop control register.
+ * Addresses configured to start tracing go from bit 0 to n-1,
+ * while those configured to stop tracing from 16 to 16 + n-1.
+ */
+ shift = (type == ETM_ADDR_TYPE_START ? 0 : 16);
+ config->vissctlr |= BIT(shift + comparator);
+}
+
+static void etm4_set_default_filter(struct etmv4_config *config)
+{
+ u64 start, stop;
+
+ /*
+ * Configure address range comparator '0' to encompass all
+ * possible addresses.
+ */
+ start = 0x0;
+ stop = ~0x0;
+
+ etm4_set_comparator_filter(config, start, stop,
+ ETM_DEFAULT_ADDR_COMP);
/*
* TRCVICTLR::SSSTATUS == 1, the start-stop logic is
@@ -601,43 +711,156 @@ static void etm4_set_default(struct etmv4_config *config)
*/
config->vinst_ctrl |= BIT(9);
+ /* No start-stop filtering for ViewInst */
+ config->vissctlr = 0x0;
+}
+
+static void etm4_set_default(struct etmv4_config *config)
+{
+ if (WARN_ON_ONCE(!config))
+ return;
+
/*
- * Configure address range comparator '0' to encompass all
- * possible addresses.
+ * Make default initialisation trace everything
+ *
+ * Select the "always true" resource selector on the
+ * "Enablign Event" line and configure address range comparator
+ * '0' to trace all the possible address range. From there
+ * configure the "include/exclude" engine to include address
+ * range comparator '0'.
*/
+ etm4_set_default_config(config);
+ etm4_set_default_filter(config);
+}
- /* First half of default address comparator: start at address 0 */
- config->addr_val[ETM_DEFAULT_ADDR_COMP] = 0x0;
- /* trace instruction addresses */
- config->addr_acc[ETM_DEFAULT_ADDR_COMP] &= ~(BIT(0) | BIT(1));
- /* EXLEVEL_NS, bits[12:15], only trace application and kernel space */
- config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= ETM_EXLEVEL_NS_HYP;
- /* EXLEVEL_S, bits[11:8], don't trace anything in secure state */
- config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= (ETM_EXLEVEL_S_APP |
- ETM_EXLEVEL_S_OS |
- ETM_EXLEVEL_S_HYP);
- config->addr_type[ETM_DEFAULT_ADDR_COMP] = ETM_ADDR_TYPE_RANGE;
+static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type)
+{
+ int nr_comparator, index = 0;
+ struct etmv4_config *config = &drvdata->config;
/*
- * Second half of default address comparator: go all
- * the way to the top.
- */
- config->addr_val[ETM_DEFAULT_ADDR_COMP + 1] = ~0x0;
- /* trace instruction addresses */
- config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] &= ~(BIT(0) | BIT(1));
- /* Address comparator type must be equal for both halves */
- config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] =
- config->addr_acc[ETM_DEFAULT_ADDR_COMP];
- config->addr_type[ETM_DEFAULT_ADDR_COMP + 1] = ETM_ADDR_TYPE_RANGE;
+ * nr_addr_cmp holds the number of comparator _pair_, so time 2
+ * for the total number of comparators.
+ */
+ nr_comparator = drvdata->nr_addr_cmp * 2;
+
+ /* Go through the tally of comparators looking for a free one. */
+ while (index < nr_comparator) {
+ switch (type) {
+ case ETM_ADDR_TYPE_RANGE:
+ if (config->addr_type[index] == ETM_ADDR_TYPE_NONE &&
+ config->addr_type[index + 1] == ETM_ADDR_TYPE_NONE)
+ return index;
+
+ /* Address range comparators go in pairs */
+ index += 2;
+ break;
+ case ETM_ADDR_TYPE_START:
+ case ETM_ADDR_TYPE_STOP:
+ if (config->addr_type[index] == ETM_ADDR_TYPE_NONE)
+ return index;
+
+ /* Start/stop address can have odd indexes */
+ index += 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* If we are here all the comparators have been used. */
+ return -ENOSPC;
+}
+
+static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
+ struct perf_event *event)
+{
+ int i, comparator, ret = 0;
+ u64 address;
+ struct etmv4_config *config = &drvdata->config;
+ struct etm_filters *filters = event->hw.addr_filters;
+
+ if (!filters)
+ goto default_filter;
+
+ /* Sync events with what Perf got */
+ perf_event_addr_filters_sync(event);
/*
- * Configure the ViewInst function to filter on address range
- * comparator '0'.
+ * If there are no filters to deal with simply go ahead with
+ * the default filter, i.e the entire address range.
*/
- config->viiectlr = BIT(0);
+ if (!filters->nr_filters)
+ goto default_filter;
+
+ for (i = 0; i < filters->nr_filters; i++) {
+ struct etm_filter *filter = &filters->etm_filter[i];
+ enum etm_addr_type type = filter->type;
+
+ /* See if a comparator is free. */
+ comparator = etm4_get_next_comparator(drvdata, type);
+ if (comparator < 0) {
+ ret = comparator;
+ goto out;
+ }
+
+ switch (type) {
+ case ETM_ADDR_TYPE_RANGE:
+ etm4_set_comparator_filter(config,
+ filter->start_addr,
+ filter->stop_addr,
+ comparator);
+ /*
+ * TRCVICTLR::SSSTATUS == 1, the start-stop logic is
+ * in the started state
+ */
+ config->vinst_ctrl |= BIT(9);
+
+ /* No start-stop filtering for ViewInst */
+ config->vissctlr = 0x0;
+ break;
+ case ETM_ADDR_TYPE_START:
+ case ETM_ADDR_TYPE_STOP:
+ /* Get the right start or stop address */
+ address = (type == ETM_ADDR_TYPE_START ?
+ filter->start_addr :
+ filter->stop_addr);
+
+ /* Configure comparator */
+ etm4_set_start_stop_filter(config, address,
+ comparator, type);
+
+ /*
+ * If filters::ssstatus == 1, trace acquisition was
+ * started but the process was yanked away before the
+ * the stop address was hit. As such the start/stop
+ * logic needs to be re-started so that tracing can
+ * resume where it left.
+ *
+ * The start/stop logic status when a process is
+ * scheduled out is checked in function
+ * etm4_disable_perf().
+ */
+ if (filters->ssstatus)
+ config->vinst_ctrl |= BIT(9);
+
+ /* No include/exclude filtering for ViewInst */
+ config->viiectlr = 0x0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ }
- /* no start-stop filtering for ViewInst */
- config->vissctlr = 0x0;
+ goto out;
+
+
+default_filter:
+ etm4_set_default_filter(config);
+
+out:
+ return ret;
}
void etm4_config_trace_mode(struct etmv4_config *config)
@@ -727,13 +950,9 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL;
struct etmv4_drvdata *drvdata;
struct resource *res = &adev->res;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
@@ -788,13 +1007,13 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
etm4_init_trace_id(drvdata);
etm4_set_default(&drvdata->config);
- desc->type = CORESIGHT_DEV_TYPE_SOURCE;
- desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
- desc->ops = &etm4_cs_ops;
- desc->pdata = pdata;
- desc->dev = dev;
- desc->groups = coresight_etmv4_groups;
- drvdata->csdev = coresight_register(desc);
+ desc.type = CORESIGHT_DEV_TYPE_SOURCE;
+ desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
+ desc.ops = &etm4_cs_ops;
+ desc.pdata = pdata;
+ desc.dev = dev;
+ desc.groups = coresight_etmv4_groups;
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto err_arch_supported;
@@ -826,12 +1045,12 @@ err_arch_supported:
}
static struct amba_id etm4_ids[] = {
- { /* ETM 4.0 - Qualcomm */
- .id = 0x0003b95d,
- .mask = 0x0003ffff,
+ { /* ETM 4.0 - Cortex-A53 */
+ .id = 0x000bb95d,
+ .mask = 0x000fffff,
.data = "ETM 4.0",
},
- { /* ETM 4.0 - Juno board */
+ { /* ETM 4.0 - Cortex-A57 */
.id = 0x000bb95e,
.mask = 0x000fffff,
.data = "ETM 4.0",
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
index 5359c51..ba8d3f8 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.h
+++ b/drivers/hwtracing/coresight/coresight-etm4x.h
@@ -183,6 +183,9 @@
#define TRCSTATR_IDLE_BIT 0
#define ETM_DEFAULT_ADDR_COMP 0
+/* PowerDown Control Register bits */
+#define TRCPDCR_PU BIT(3)
+
/* secure state access levels */
#define ETM_EXLEVEL_S_APP BIT(8)
#define ETM_EXLEVEL_S_OS BIT(9)
@@ -407,14 +410,6 @@ enum etm_addr_ctxtype {
ETM_CTX_CTXID_VMID,
};
-enum etm_addr_type {
- ETM_ADDR_TYPE_NONE,
- ETM_ADDR_TYPE_SINGLE,
- ETM_ADDR_TYPE_RANGE,
- ETM_ADDR_TYPE_START,
- ETM_ADDR_TYPE_STOP,
-};
-
extern const struct attribute_group *coresight_etmv4_groups[];
void etm4_config_trace_mode(struct etmv4_config *config);
#endif
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 05df789..860fe6e 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -176,7 +176,7 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL;
struct funnel_drvdata *drvdata;
struct resource *res = &adev->res;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
if (np) {
@@ -207,17 +207,13 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->base = base;
pm_runtime_put(&adev->dev);
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
- desc->type = CORESIGHT_DEV_TYPE_LINK;
- desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
- desc->ops = &funnel_cs_ops;
- desc->pdata = pdata;
- desc->dev = dev;
- desc->groups = coresight_funnel_groups;
- drvdata->csdev = coresight_register(desc);
+ desc.type = CORESIGHT_DEV_TYPE_LINK;
+ desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
+ desc.ops = &funnel_cs_ops;
+ desc.pdata = pdata;
+ desc.dev = dev;
+ desc.groups = coresight_funnel_groups;
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index ad975c5..196a14b 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -16,6 +16,7 @@
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/coresight.h>
+#include <linux/pm_runtime.h>
/*
* Coresight management registers (0xf00-0xfcc)
@@ -37,16 +38,32 @@
#define ETM_MODE_EXCL_KERN BIT(30)
#define ETM_MODE_EXCL_USER BIT(31)
-#define coresight_simple_func(type, name, offset) \
+typedef u32 (*coresight_read_fn)(const struct device *, u32 offset);
+#define coresight_simple_func(type, func, name, offset) \
static ssize_t name##_show(struct device *_dev, \
struct device_attribute *attr, char *buf) \
{ \
type *drvdata = dev_get_drvdata(_dev->parent); \
- return scnprintf(buf, PAGE_SIZE, "0x%x\n", \
- readl_relaxed(drvdata->base + offset)); \
+ coresight_read_fn fn = func; \
+ u32 val; \
+ pm_runtime_get_sync(_dev->parent); \
+ if (fn) \
+ val = fn(_dev->parent, offset); \
+ else \
+ val = readl_relaxed(drvdata->base + offset); \
+ pm_runtime_put_sync(_dev->parent); \
+ return scnprintf(buf, PAGE_SIZE, "0x%x\n", val); \
} \
static DEVICE_ATTR_RO(name)
+enum etm_addr_type {
+ ETM_ADDR_TYPE_NONE,
+ ETM_ADDR_TYPE_SINGLE,
+ ETM_ADDR_TYPE_RANGE,
+ ETM_ADDR_TYPE_START,
+ ETM_ADDR_TYPE_STOP,
+};
+
enum cs_mode {
CS_MODE_DISABLED,
CS_MODE_SYSFS,
diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
index 700f710..0a3d15f 100644
--- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c
+++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c
@@ -102,7 +102,7 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
struct resource *res = &adev->res;
struct coresight_platform_data *pdata = NULL;
struct replicator_state *drvdata;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
void __iomem *base;
@@ -134,16 +134,12 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
dev_set_drvdata(dev, drvdata);
pm_runtime_put(&adev->dev);
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
- desc->type = CORESIGHT_DEV_TYPE_LINK;
- desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
- desc->ops = &replicator_cs_ops;
- desc->pdata = adev->dev.platform_data;
- desc->dev = &adev->dev;
- drvdata->csdev = coresight_register(desc);
+ desc.type = CORESIGHT_DEV_TYPE_LINK;
+ desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
+ desc.ops = &replicator_cs_ops;
+ desc.pdata = adev->dev.platform_data;
+ desc.dev = &adev->dev;
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
index c6982e3..3756e71 100644
--- a/drivers/hwtracing/coresight/coresight-replicator.c
+++ b/drivers/hwtracing/coresight/coresight-replicator.c
@@ -69,7 +69,7 @@ static int replicator_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata = NULL;
struct replicator_drvdata *drvdata;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = pdev->dev.of_node;
if (np) {
@@ -95,18 +95,12 @@ static int replicator_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
platform_set_drvdata(pdev, drvdata);
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc) {
- ret = -ENOMEM;
- goto out_disable_pm;
- }
-
- desc->type = CORESIGHT_DEV_TYPE_LINK;
- desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
- desc->ops = &replicator_cs_ops;
- desc->pdata = pdev->dev.platform_data;
- desc->dev = &pdev->dev;
- drvdata->csdev = coresight_register(desc);
+ desc.type = CORESIGHT_DEV_TYPE_LINK;
+ desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
+ desc.ops = &replicator_cs_ops;
+ desc.pdata = pdev->dev.platform_data;
+ desc.dev = &pdev->dev;
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto out_disable_pm;
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index 73be58a..49e0f1b 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -105,10 +105,12 @@ module_param_named(
/**
* struct channel_space - central management entity for extended ports
* @base: memory mapped base address where channels start.
+ * @phys: physical base address of channel region.
* @guaraneed: is the channel delivery guaranteed.
*/
struct channel_space {
void __iomem *base;
+ phys_addr_t phys;
unsigned long *guaranteed;
};
@@ -196,7 +198,7 @@ static void stm_enable_hw(struct stm_drvdata *drvdata)
}
static int stm_enable(struct coresight_device *csdev,
- struct perf_event_attr *attr, u32 mode)
+ struct perf_event *event, u32 mode)
{
u32 val;
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -258,7 +260,8 @@ static void stm_disable_hw(struct stm_drvdata *drvdata)
stm_hwevent_disable_hw(drvdata);
}
-static void stm_disable(struct coresight_device *csdev)
+static void stm_disable(struct coresight_device *csdev,
+ struct perf_event *event)
{
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@@ -353,7 +356,24 @@ static void stm_generic_unlink(struct stm_data *stm_data,
if (!drvdata || !drvdata->csdev)
return;
- stm_disable(drvdata->csdev);
+ stm_disable(drvdata->csdev, NULL);
+}
+
+static phys_addr_t
+stm_mmio_addr(struct stm_data *stm_data, unsigned int master,
+ unsigned int channel, unsigned int nr_chans)
+{
+ struct stm_drvdata *drvdata = container_of(stm_data,
+ struct stm_drvdata, stm);
+ phys_addr_t addr;
+
+ addr = drvdata->chs.phys + channel * BYTES_PER_CHANNEL;
+
+ if (offset_in_page(addr) ||
+ offset_in_page(nr_chans * BYTES_PER_CHANNEL))
+ return 0;
+
+ return addr;
}
static long stm_generic_set_options(struct stm_data *stm_data,
@@ -616,7 +636,7 @@ static ssize_t traceid_store(struct device *dev,
static DEVICE_ATTR_RW(traceid);
#define coresight_stm_simple_func(name, offset) \
- coresight_simple_func(struct stm_drvdata, name, offset)
+ coresight_simple_func(struct stm_drvdata, NULL, name, offset)
coresight_stm_simple_func(tcsr, STMTCSR);
coresight_stm_simple_func(tsfreqr, STMTSFREQR);
@@ -761,7 +781,9 @@ static void stm_init_generic_data(struct stm_drvdata *drvdata)
drvdata->stm.sw_end = 1;
drvdata->stm.hw_override = true;
drvdata->stm.sw_nchannels = drvdata->numsp;
+ drvdata->stm.sw_mmiosz = BYTES_PER_CHANNEL;
drvdata->stm.packet = stm_generic_packet;
+ drvdata->stm.mmio_addr = stm_mmio_addr;
drvdata->stm.link = stm_generic_link;
drvdata->stm.unlink = stm_generic_unlink;
drvdata->stm.set_options = stm_generic_set_options;
@@ -778,7 +800,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
struct resource *res = &adev->res;
struct resource ch_res;
size_t res_size, bitmap_size;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
if (np) {
@@ -808,6 +830,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res);
if (ret)
return ret;
+ drvdata->chs.phys = ch_res.start;
base = devm_ioremap_resource(dev, &ch_res);
if (IS_ERR(base))
@@ -843,19 +866,13 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id)
return -EPROBE_DEFER;
}
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc) {
- ret = -ENOMEM;
- goto stm_unregister;
- }
-
- desc->type = CORESIGHT_DEV_TYPE_SOURCE;
- desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE;
- desc->ops = &stm_cs_ops;
- desc->pdata = pdata;
- desc->dev = dev;
- desc->groups = coresight_stm_groups;
- drvdata->csdev = coresight_register(desc);
+ desc.type = CORESIGHT_DEV_TYPE_SOURCE;
+ desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE;
+ desc.ops = &stm_cs_ops;
+ desc.pdata = pdata;
+ desc.dev = dev;
+ desc.groups = coresight_stm_groups;
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto stm_unregister;
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index 466af86..d6941ea 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -22,7 +22,7 @@
#include "coresight-priv.h"
#include "coresight-tmc.h"
-void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
+static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
@@ -48,6 +48,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
int i;
bufp = drvdata->buf;
+ drvdata->len = 0;
while (1) {
for (i = 0; i < drvdata->memwidth; i++) {
read_data = readl_relaxed(drvdata->base + TMC_RRD);
@@ -55,6 +56,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata)
return;
memcpy(bufp, &read_data, 4);
bufp += 4;
+ drvdata->len += 4;
}
}
}
@@ -166,7 +168,7 @@ out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Free memory outside the spinlock if need be */
- if (!used && buf)
+ if (!used)
kfree(buf);
if (!ret)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 688be9e..886ea83 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -20,7 +20,7 @@
#include "coresight-priv.h"
#include "coresight-tmc.h"
-void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
+static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
{
u32 axictl;
@@ -64,11 +64,17 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
rwp = readl_relaxed(drvdata->base + TMC_RWP);
val = readl_relaxed(drvdata->base + TMC_STS);
- /* How much memory do we still have */
- if (val & BIT(0))
+ /*
+ * Adjust the buffer to point to the beginning of the trace data
+ * and update the available trace data.
+ */
+ if (val & TMC_STS_FULL) {
drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
- else
+ drvdata->len = drvdata->size;
+ } else {
drvdata->buf = drvdata->vaddr;
+ drvdata->len = rwp - drvdata->paddr;
+ }
}
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index 9e02ac9..d8517d2 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -38,8 +38,7 @@ void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
if (coresight_timeout(drvdata->base,
TMC_STS, TMC_STS_TMCREADY_BIT, 1)) {
dev_err(drvdata->dev,
- "timeout observed when probing at offset %#x\n",
- TMC_STS);
+ "timeout while waiting for TMC to be Ready\n");
}
}
@@ -56,8 +55,7 @@ void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
if (coresight_timeout(drvdata->base,
TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) {
dev_err(drvdata->dev,
- "timeout observed when probing at offset %#x\n",
- TMC_FFCR);
+ "timeout while waiting for completion of Manual Flush\n");
}
tmc_wait_for_tmcready(drvdata);
@@ -140,8 +138,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
struct tmc_drvdata, miscdev);
char *bufp = drvdata->buf + *ppos;
- if (*ppos + len > drvdata->size)
- len = drvdata->size - *ppos;
+ if (*ppos + len > drvdata->len)
+ len = drvdata->len - *ppos;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
if (bufp == (char *)(drvdata->vaddr + drvdata->size))
@@ -160,7 +158,7 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
*ppos += len;
dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n",
- __func__, len, (int)(drvdata->size - *ppos));
+ __func__, len, (int)(drvdata->len - *ppos));
return len;
}
@@ -220,7 +218,7 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid)
}
#define coresight_tmc_simple_func(name, offset) \
- coresight_simple_func(struct tmc_drvdata, name, offset)
+ coresight_simple_func(struct tmc_drvdata, NULL, name, offset)
coresight_tmc_simple_func(rsz, TMC_RSZ);
coresight_tmc_simple_func(sts, TMC_STS);
@@ -249,8 +247,8 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = {
NULL,
};
-ssize_t trigger_cntr_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t trigger_cntr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->trigger_cntr;
@@ -304,27 +302,32 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL;
struct tmc_drvdata *drvdata;
struct resource *res = &adev->res;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
if (np) {
pdata = of_get_coresight_platform_data(dev, np);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
+ if (IS_ERR(pdata)) {
+ ret = PTR_ERR(pdata);
+ goto out;
+ }
adev->dev.platform_data = pdata;
}
+ ret = -ENOMEM;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
- return -ENOMEM;
+ goto out;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base))
- return PTR_ERR(base);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto out;
+ }
drvdata->base = base;
@@ -347,33 +350,28 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
pm_runtime_put(&adev->dev);
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc) {
- ret = -ENOMEM;
- goto err_devm_kzalloc;
- }
-
- desc->pdata = pdata;
- desc->dev = dev;
- desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
- desc->groups = coresight_tmc_groups;
+ desc.pdata = pdata;
+ desc.dev = dev;
+ desc.groups = coresight_tmc_groups;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) {
- desc->type = CORESIGHT_DEV_TYPE_SINK;
- desc->ops = &tmc_etb_cs_ops;
+ desc.type = CORESIGHT_DEV_TYPE_SINK;
+ desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
+ desc.ops = &tmc_etb_cs_ops;
} else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
- desc->type = CORESIGHT_DEV_TYPE_SINK;
- desc->ops = &tmc_etr_cs_ops;
+ desc.type = CORESIGHT_DEV_TYPE_SINK;
+ desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
+ desc.ops = &tmc_etr_cs_ops;
} else {
- desc->type = CORESIGHT_DEV_TYPE_LINKSINK;
- desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
- desc->ops = &tmc_etf_cs_ops;
+ desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
+ desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
+ desc.ops = &tmc_etf_cs_ops;
}
- drvdata->csdev = coresight_register(desc);
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
- goto err_devm_kzalloc;
+ goto out;
}
drvdata->miscdev.name = pdata->name;
@@ -381,16 +379,8 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->miscdev.fops = &tmc_fops;
ret = misc_register(&drvdata->miscdev);
if (ret)
- goto err_misc_register;
-
- return 0;
-
-err_misc_register:
- coresight_unregister(drvdata->csdev);
-err_devm_kzalloc:
- if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
- dma_free_coherent(dev, drvdata->size,
- drvdata->vaddr, drvdata->paddr);
+ coresight_unregister(drvdata->csdev);
+out:
return ret;
}
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 5c5fe2a..44b3ae3 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -98,7 +98,8 @@ enum tmc_mem_intf_width {
* @buf: area of memory where trace data get sent.
* @paddr: DMA start location in RAM.
* @vaddr: virtual representation of @paddr.
- * @size: @buf size.
+ * @size: trace buffer size.
+ * @len: size of the available trace.
* @mode: how this TMC is being used.
* @config_type: TMC variant, must be of type @tmc_config_type.
* @memwidth: width of the memory interface databus, in bytes.
@@ -115,6 +116,7 @@ struct tmc_drvdata {
dma_addr_t paddr;
void __iomem *vaddr;
u32 size;
+ u32 len;
local_t mode;
enum tmc_config_type config_type;
enum tmc_mem_intf_width memwidth;
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index 4e471e2..0673baf 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -119,7 +119,7 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata = NULL;
struct tpiu_drvdata *drvdata;
struct resource *res = &adev->res;
- struct coresight_desc *desc;
+ struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
if (np) {
@@ -154,16 +154,12 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
pm_runtime_put(&adev->dev);
- desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return -ENOMEM;
-
- desc->type = CORESIGHT_DEV_TYPE_SINK;
- desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT;
- desc->ops = &tpiu_cs_ops;
- desc->pdata = pdata;
- desc->dev = dev;
- drvdata->csdev = coresight_register(desc);
+ desc.type = CORESIGHT_DEV_TYPE_SINK;
+ desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_PORT;
+ desc.ops = &tpiu_cs_ops;
+ desc.pdata = pdata;
+ desc.dev = dev;
+ drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
index d08d1ab..7bf00a0 100644
--- a/drivers/hwtracing/coresight/coresight.c
+++ b/drivers/hwtracing/coresight/coresight.c
@@ -257,7 +257,7 @@ static void coresight_disable_source(struct coresight_device *csdev)
{
if (atomic_dec_return(csdev->refcnt) == 0) {
if (source_ops(csdev)->disable) {
- source_ops(csdev)->disable(csdev);
+ source_ops(csdev)->disable(csdev, NULL);
csdev->enable = false;
}
}
@@ -429,7 +429,7 @@ struct list_head *coresight_build_path(struct coresight_device *csdev)
path = kzalloc(sizeof(struct list_head), GFP_KERNEL);
if (!path)
- return NULL;
+ return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(path);
@@ -725,7 +725,8 @@ static int coresight_orphan_match(struct device *dev, void *data)
/* We have found at least one orphan connection */
if (conn->child_dev == NULL) {
/* Does it match this newly added device? */
- if (!strcmp(dev_name(&csdev->dev), conn->child_name)) {
+ if (conn->child_name &&
+ !strcmp(dev_name(&csdev->dev), conn->child_name)) {
conn->child_dev = csdev;
} else {
/* This component still has an orphan */
@@ -893,7 +894,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
int nr_refcnts = 1;
atomic_t *refcnts = NULL;
struct coresight_device *csdev;
- struct coresight_connection *conns;
+ struct coresight_connection *conns = NULL;
csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
if (!csdev) {
@@ -921,16 +922,20 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
csdev->nr_inport = desc->pdata->nr_inport;
csdev->nr_outport = desc->pdata->nr_outport;
- conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL);
- if (!conns) {
- ret = -ENOMEM;
- goto err_kzalloc_conns;
- }
- for (i = 0; i < csdev->nr_outport; i++) {
- conns[i].outport = desc->pdata->outports[i];
- conns[i].child_name = desc->pdata->child_names[i];
- conns[i].child_port = desc->pdata->child_ports[i];
+ /* Initialise connections if there is at least one outport */
+ if (csdev->nr_outport) {
+ conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL);
+ if (!conns) {
+ ret = -ENOMEM;
+ goto err_kzalloc_conns;
+ }
+
+ for (i = 0; i < csdev->nr_outport; i++) {
+ conns[i].outport = desc->pdata->outports[i];
+ conns[i].child_name = desc->pdata->child_names[i];
+ conns[i].child_port = desc->pdata->child_ports[i];
+ }
}
csdev->conns = conns;
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
index b68da18..629e031 100644
--- a/drivers/hwtracing/coresight/of_coresight.c
+++ b/drivers/hwtracing/coresight/of_coresight.c
@@ -166,7 +166,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
rdev = of_coresight_get_endpoint_device(rparent);
if (!rdev)
- continue;
+ return ERR_PTR(-EPROBE_DEFER);
pdata->child_names[i] = dev_name(rdev);
pdata->child_ports[i] = rendpoint.id;
@@ -184,6 +184,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
break;
}
}
+ of_node_put(dn);
return pdata;
}
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 5ef9b73..26298af 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -1486,7 +1486,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
priv->features |= FEATURE_IRQ;
priv->features |= FEATURE_SMBUS_PEC;
priv->features |= FEATURE_BLOCK_BUFFER;
- priv->features |= FEATURE_TCO;
+ /* If we have ACPI based watchdog use that instead */
+ if (!acpi_has_watchdog())
+ priv->features |= FEATURE_TCO;
priv->features |= FEATURE_HOST_NOTIFY;
break;
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index d130cdc..7fa65ab 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -8,8 +8,6 @@ menu "Pressure sensors"
config BMP280
tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver"
depends on (I2C || SPI_MASTER)
- depends on !(BMP085_I2C=y || BMP085_I2C=m)
- depends on !(BMP085_SPI=y || BMP085_SPI=m)
select REGMAP
select BMP280_I2C if (I2C)
select BMP280_SPI if (SPI_MASTER)
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 5d11fea..f3135ae 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -950,6 +950,12 @@ static const struct input_device_id joydev_ids[] = {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT_MASK(EV_ABS) },
+ .absbit = { BIT_MASK(ABS_Z) },
+ },
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT_MASK(EV_ABS) },
.absbit = { BIT_MASK(ABS_WHEEL) },
},
{
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 65ebbd1..92595b9 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -1013,23 +1013,12 @@ static struct miscdevice uinput_misc = {
.minor = UINPUT_MINOR,
.name = UINPUT_NAME,
};
+module_misc_device(uinput_misc);
+
MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
MODULE_ALIAS("devname:" UINPUT_NAME);
-static int __init uinput_init(void)
-{
- return misc_register(&uinput_misc);
-}
-
-static void __exit uinput_exit(void)
-{
- misc_deregister(&uinput_misc);
-}
-
MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
MODULE_DESCRIPTION("User level driver support for input subsystem");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.3");
-
-module_init(uinput_init);
-module_exit(uinput_exit);
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 96de97a..4025291 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -940,15 +940,13 @@ static void build_inv_irt(struct iommu_cmd *cmd, u16 devid)
* Writes the command to the IOMMUs command buffer and informs the
* hardware about the new command.
*/
-static int iommu_queue_command_sync(struct amd_iommu *iommu,
- struct iommu_cmd *cmd,
- bool sync)
+static int __iommu_queue_command_sync(struct amd_iommu *iommu,
+ struct iommu_cmd *cmd,
+ bool sync)
{
u32 left, tail, head, next_tail;
- unsigned long flags;
again:
- spin_lock_irqsave(&iommu->lock, flags);
head = readl(iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
tail = readl(iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
@@ -957,15 +955,14 @@ again:
if (left <= 2) {
struct iommu_cmd sync_cmd;
- volatile u64 sem = 0;
int ret;
- build_completion_wait(&sync_cmd, (u64)&sem);
- copy_cmd_to_buffer(iommu, &sync_cmd, tail);
+ iommu->cmd_sem = 0;
- spin_unlock_irqrestore(&iommu->lock, flags);
+ build_completion_wait(&sync_cmd, (u64)&iommu->cmd_sem);
+ copy_cmd_to_buffer(iommu, &sync_cmd, tail);
- if ((ret = wait_on_sem(&sem)) != 0)
+ if ((ret = wait_on_sem(&iommu->cmd_sem)) != 0)
return ret;
goto again;
@@ -976,9 +973,21 @@ again:
/* We need to sync now to make sure all commands are processed */
iommu->need_sync = sync;
+ return 0;
+}
+
+static int iommu_queue_command_sync(struct amd_iommu *iommu,
+ struct iommu_cmd *cmd,
+ bool sync)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&iommu->lock, flags);
+ ret = __iommu_queue_command_sync(iommu, cmd, sync);
spin_unlock_irqrestore(&iommu->lock, flags);
- return 0;
+ return ret;
}
static int iommu_queue_command(struct amd_iommu *iommu, struct iommu_cmd *cmd)
@@ -993,19 +1002,29 @@ static int iommu_queue_command(struct amd_iommu *iommu, struct iommu_cmd *cmd)
static int iommu_completion_wait(struct amd_iommu *iommu)
{
struct iommu_cmd cmd;
- volatile u64 sem = 0;
+ unsigned long flags;
int ret;
if (!iommu->need_sync)
return 0;
- build_completion_wait(&cmd, (u64)&sem);
- ret = iommu_queue_command_sync(iommu, &cmd, false);
+ build_completion_wait(&cmd, (u64)&iommu->cmd_sem);
+
+ spin_lock_irqsave(&iommu->lock, flags);
+
+ iommu->cmd_sem = 0;
+
+ ret = __iommu_queue_command_sync(iommu, &cmd, false);
if (ret)
- return ret;
+ goto out_unlock;
+
+ ret = wait_on_sem(&iommu->cmd_sem);
- return wait_on_sem(&sem);
+out_unlock:
+ spin_unlock_irqrestore(&iommu->lock, flags);
+
+ return ret;
}
static int iommu_flush_dte(struct amd_iommu *iommu, u16 devid)
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index caf5e38..9652848 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -524,6 +524,8 @@ struct amd_iommu {
struct irq_domain *ir_domain;
struct irq_domain *msi_domain;
#endif
+
+ volatile u64 __aligned(8) cmd_sem;
};
#define ACPIHID_UID_LEN 256
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 7f87289..82b0b5d 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -39,6 +39,7 @@ config ARM_GIC_V3_ITS
bool
depends on PCI
depends on PCI_MSI
+ select ACPI_IORT if ACPI
config ARM_NVIC
bool
@@ -156,6 +157,13 @@ config PIC32_EVIC
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
+config JCORE_AIC
+ bool "J-Core integrated AIC"
+ depends on OF && (SUPERH || COMPILE_TEST)
+ select IRQ_DOMAIN
+ help
+ Support for the J-Core integrated AIC.
+
config RENESAS_INTC_IRQPIN
bool
select IRQ_DOMAIN
@@ -251,6 +259,9 @@ config IRQ_MXS
config MVEBU_ODMI
bool
+config MVEBU_PIC
+ bool
+
config LS_SCFG_MSI
def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE
depends on PCI && PCI_MSI
@@ -264,3 +275,7 @@ config EZNPS_GIC
select IRQ_DOMAIN
help
Support the EZchip NPS400 global interrupt controller
+
+config STM32_EXTI
+ bool
+ select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 4c203b6..b372e79 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_I8259) += irq-i8259.o
obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o
obj-$(CONFIG_IRQ_MIPS_CPU) += irq-mips-cpu.o
obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o
+obj-$(CONFIG_JCORE_AIC) += irq-jcore-aic.o
obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o
obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
@@ -68,6 +69,8 @@ obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o
obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o
obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o
obj-$(CONFIG_MVEBU_ODMI) += irq-mvebu-odmi.o
+obj-$(CONFIG_MVEBU_PIC) += irq-mvebu-pic.o
obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o
obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
+obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o
diff --git a/drivers/irqchip/irq-gic-pm.c b/drivers/irqchip/irq-gic-pm.c
index 4cbffba..ecafd29 100644
--- a/drivers/irqchip/irq-gic-pm.c
+++ b/drivers/irqchip/irq-gic-pm.c
@@ -64,7 +64,6 @@ static int gic_runtime_suspend(struct device *dev)
static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data)
{
- struct clk *clk;
unsigned int i;
int ret;
@@ -76,28 +75,16 @@ static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data)
return ret;
for (i = 0; i < data->num_clocks; i++) {
- clk = of_clk_get_by_name(dev->of_node, data->clocks[i]);
- if (IS_ERR(clk)) {
- dev_err(dev, "failed to get clock %s\n",
- data->clocks[i]);
- ret = PTR_ERR(clk);
- goto error;
- }
-
- ret = pm_clk_add_clk(dev, clk);
+ ret = of_pm_clk_add_clk(dev, data->clocks[i]);
if (ret) {
- dev_err(dev, "failed to add clock at index %d\n", i);
- clk_put(clk);
- goto error;
+ dev_err(dev, "failed to add clock %s\n",
+ data->clocks[i]);
+ pm_clk_destroy(dev);
+ return ret;
}
}
return 0;
-
-error:
- pm_clk_destroy(dev);
-
- return ret;
}
static int gic_probe(struct platform_device *pdev)
diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
index aee60ed..aee1c60 100644
--- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c
+++ b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
@@ -15,6 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/acpi_iort.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -106,34 +107,91 @@ static struct of_device_id its_device_id[] = {
{},
};
-static int __init its_pci_msi_init(void)
+static int __init its_pci_msi_init_one(struct fwnode_handle *handle,
+ const char *name)
{
- struct device_node *np;
struct irq_domain *parent;
+ parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
+ if (!parent || !msi_get_domain_info(parent)) {
+ pr_err("%s: Unable to locate ITS domain\n", name);
+ return -ENXIO;
+ }
+
+ if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,
+ parent)) {
+ pr_err("%s: Unable to create PCI domain\n", name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int __init its_pci_of_msi_init(void)
+{
+ struct device_node *np;
+
for (np = of_find_matching_node(NULL, its_device_id); np;
np = of_find_matching_node(np, its_device_id)) {
if (!of_property_read_bool(np, "msi-controller"))
continue;
- parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
- if (!parent || !msi_get_domain_info(parent)) {
- pr_err("%s: unable to locate ITS domain\n",
- np->full_name);
- continue;
- }
-
- if (!pci_msi_create_irq_domain(of_node_to_fwnode(np),
- &its_pci_msi_domain_info,
- parent)) {
- pr_err("%s: unable to create PCI domain\n",
- np->full_name);
+ if (its_pci_msi_init_one(of_node_to_fwnode(np), np->full_name))
continue;
- }
pr_info("PCI/MSI: %s domain created\n", np->full_name);
}
return 0;
}
+
+#ifdef CONFIG_ACPI
+
+static int __init
+its_pci_msi_parse_madt(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_translator *its_entry;
+ struct fwnode_handle *dom_handle;
+ const char *node_name;
+ int err = -ENXIO;
+
+ its_entry = (struct acpi_madt_generic_translator *)header;
+ node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
+ (long)its_entry->base_address);
+ dom_handle = iort_find_domain_token(its_entry->translation_id);
+ if (!dom_handle) {
+ pr_err("%s: Unable to locate ITS domain handle\n", node_name);
+ goto out;
+ }
+
+ err = its_pci_msi_init_one(dom_handle, node_name);
+ if (!err)
+ pr_info("PCI/MSI: %s domain created\n", node_name);
+
+out:
+ kfree(node_name);
+ return err;
+}
+
+static int __init its_pci_acpi_msi_init(void)
+{
+ acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
+ its_pci_msi_parse_madt, 0);
+ return 0;
+}
+#else
+static int __init its_pci_acpi_msi_init(void)
+{
+ return 0;
+}
+#endif
+
+static int __init its_pci_msi_init(void)
+{
+ its_pci_of_msi_init();
+ its_pci_acpi_msi_init();
+
+ return 0;
+}
early_initcall(its_pci_msi_init);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 36b9c28..35c851c 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -15,10 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/acpi_iort.h>
#include <linux/log2.h>
#include <linux/mm.h>
#include <linux/msi.h>
@@ -75,7 +78,7 @@ struct its_node {
raw_spinlock_t lock;
struct list_head entry;
void __iomem *base;
- unsigned long phys_base;
+ phys_addr_t phys_base;
struct its_cmd_block *cmd_base;
struct its_cmd_block *cmd_write;
struct its_baser tables[GITS_BASER_NR_REGS];
@@ -115,6 +118,7 @@ struct its_device {
static LIST_HEAD(its_nodes);
static DEFINE_SPINLOCK(its_lock);
static struct rdists *gic_rdists;
+static struct irq_domain *its_parent;
#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist))
#define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base)
@@ -1437,6 +1441,11 @@ static int its_irq_gic_domain_alloc(struct irq_domain *domain,
fwspec.param[0] = GIC_IRQ_TYPE_LPI;
fwspec.param[1] = hwirq;
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+ } else if (is_fwnode_irqchip(domain->parent->fwnode)) {
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = hwirq;
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
} else {
return -EINVAL;
}
@@ -1614,44 +1623,59 @@ static void its_enable_quirks(struct its_node *its)
gic_enable_quirks(iidr, its_quirks, its);
}
-static int __init its_probe(struct device_node *node,
- struct irq_domain *parent)
+static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
+{
+ struct irq_domain *inner_domain;
+ struct msi_domain_info *info;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ inner_domain = irq_domain_create_tree(handle, &its_domain_ops, its);
+ if (!inner_domain) {
+ kfree(info);
+ return -ENOMEM;
+ }
+
+ inner_domain->parent = its_parent;
+ inner_domain->bus_token = DOMAIN_BUS_NEXUS;
+ info->ops = &its_msi_domain_ops;
+ info->data = its;
+ inner_domain->host_data = info;
+
+ return 0;
+}
+
+static int __init its_probe_one(struct resource *res,
+ struct fwnode_handle *handle, int numa_node)
{
- struct resource res;
struct its_node *its;
void __iomem *its_base;
- struct irq_domain *inner_domain;
u32 val;
u64 baser, tmp;
int err;
- err = of_address_to_resource(node, 0, &res);
- if (err) {
- pr_warn("%s: no regs?\n", node->full_name);
- return -ENXIO;
- }
-
- its_base = ioremap(res.start, resource_size(&res));
+ its_base = ioremap(res->start, resource_size(res));
if (!its_base) {
- pr_warn("%s: unable to map registers\n", node->full_name);
+ pr_warn("ITS@%pa: Unable to map ITS registers\n", &res->start);
return -ENOMEM;
}
val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK;
if (val != 0x30 && val != 0x40) {
- pr_warn("%s: no ITS detected, giving up\n", node->full_name);
+ pr_warn("ITS@%pa: No ITS detected, giving up\n", &res->start);
err = -ENODEV;
goto out_unmap;
}
err = its_force_quiescent(its_base);
if (err) {
- pr_warn("%s: failed to quiesce, giving up\n",
- node->full_name);
+ pr_warn("ITS@%pa: Failed to quiesce, giving up\n", &res->start);
goto out_unmap;
}
- pr_info("ITS: %s\n", node->full_name);
+ pr_info("ITS %pR\n", res);
its = kzalloc(sizeof(*its), GFP_KERNEL);
if (!its) {
@@ -1663,9 +1687,9 @@ static int __init its_probe(struct device_node *node,
INIT_LIST_HEAD(&its->entry);
INIT_LIST_HEAD(&its->its_device_list);
its->base = its_base;
- its->phys_base = res.start;
+ its->phys_base = res->start;
its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
- its->numa_node = of_node_to_nid(node);
+ its->numa_node = numa_node;
its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL);
if (!its->cmd_base) {
@@ -1712,28 +1736,9 @@ static int __init its_probe(struct device_node *node,
writeq_relaxed(0, its->base + GITS_CWRITER);
writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
- if (of_property_read_bool(node, "msi-controller")) {
- struct msi_domain_info *info;
-
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info) {
- err = -ENOMEM;
- goto out_free_tables;
- }
-
- inner_domain = irq_domain_add_tree(node, &its_domain_ops, its);
- if (!inner_domain) {
- err = -ENOMEM;
- kfree(info);
- goto out_free_tables;
- }
-
- inner_domain->parent = parent;
- inner_domain->bus_token = DOMAIN_BUS_NEXUS;
- info->ops = &its_msi_domain_ops;
- info->data = its;
- inner_domain->host_data = info;
- }
+ err = its_init_domain(handle, its);
+ if (err)
+ goto out_free_tables;
spin_lock(&its_lock);
list_add(&its->entry, &its_nodes);
@@ -1749,7 +1754,7 @@ out_free_its:
kfree(its);
out_unmap:
iounmap(its_base);
- pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
+ pr_err("ITS@%pa: failed probing (%d)\n", &res->start, err);
return err;
}
@@ -1777,16 +1782,92 @@ static struct of_device_id its_device_id[] = {
{},
};
-int __init its_init(struct device_node *node, struct rdists *rdists,
- struct irq_domain *parent_domain)
+static int __init its_of_probe(struct device_node *node)
{
struct device_node *np;
+ struct resource res;
for (np = of_find_matching_node(node, its_device_id); np;
np = of_find_matching_node(np, its_device_id)) {
- its_probe(np, parent_domain);
+ if (!of_property_read_bool(np, "msi-controller")) {
+ pr_warn("%s: no msi-controller property, ITS ignored\n",
+ np->full_name);
+ continue;
+ }
+
+ if (of_address_to_resource(np, 0, &res)) {
+ pr_warn("%s: no regs?\n", np->full_name);
+ continue;
+ }
+
+ its_probe_one(&res, &np->fwnode, of_node_to_nid(np));
+ }
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+
+#define ACPI_GICV3_ITS_MEM_SIZE (SZ_128K)
+
+static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_translator *its_entry;
+ struct fwnode_handle *dom_handle;
+ struct resource res;
+ int err;
+
+ its_entry = (struct acpi_madt_generic_translator *)header;
+ memset(&res, 0, sizeof(res));
+ res.start = its_entry->base_address;
+ res.end = its_entry->base_address + ACPI_GICV3_ITS_MEM_SIZE - 1;
+ res.flags = IORESOURCE_MEM;
+
+ dom_handle = irq_domain_alloc_fwnode((void *)its_entry->base_address);
+ if (!dom_handle) {
+ pr_err("ITS@%pa: Unable to allocate GICv3 ITS domain token\n",
+ &res.start);
+ return -ENOMEM;
}
+ err = iort_register_domain_token(its_entry->translation_id, dom_handle);
+ if (err) {
+ pr_err("ITS@%pa: Unable to register GICv3 ITS domain token (ITS ID %d) to IORT\n",
+ &res.start, its_entry->translation_id);
+ goto dom_err;
+ }
+
+ err = its_probe_one(&res, dom_handle, NUMA_NO_NODE);
+ if (!err)
+ return 0;
+
+ iort_deregister_domain_token(its_entry->translation_id);
+dom_err:
+ irq_domain_free_fwnode(dom_handle);
+ return err;
+}
+
+static void __init its_acpi_probe(void)
+{
+ acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
+ gic_acpi_parse_madt_its, 0);
+}
+#else
+static void __init its_acpi_probe(void) { }
+#endif
+
+int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
+ struct irq_domain *parent_domain)
+{
+ struct device_node *of_node;
+
+ its_parent = parent_domain;
+ of_node = to_of_node(handle);
+ if (of_node)
+ its_of_probe(of_node);
+ else
+ its_acpi_probe();
+
if (list_empty(&its_nodes)) {
pr_warn("ITS: No ITS available, not enabling LPIs\n");
return -ENXIO;
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index da6c0ba..9b81bd8 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -495,6 +495,14 @@ static void gic_cpu_sys_reg_init(void)
/* Set priority mask register */
gic_write_pmr(DEFAULT_PMR_VALUE);
+ /*
+ * Some firmwares hand over to the kernel with the BPR changed from
+ * its reset value (and with a value large enough to prevent
+ * any pre-emptive interrupts from working at all). Writing a zero
+ * to BPR restores is reset value.
+ */
+ gic_write_bpr1(0);
+
if (static_key_true(&supports_deactivate)) {
/* EOI drops priority only (mode 1) */
gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop);
@@ -911,7 +919,6 @@ static int __init gic_init_bases(void __iomem *dist_base,
u64 redist_stride,
struct fwnode_handle *handle)
{
- struct device_node *node;
u32 typer;
int gic_irqs;
int err;
@@ -952,10 +959,8 @@ static int __init gic_init_bases(void __iomem *dist_base,
set_handle_irq(gic_handle_irq);
- node = to_of_node(handle);
- if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis() &&
- node) /* Temp hack to prevent ITS init for ACPI */
- its_init(node, &gic_data.rdists, gic_data.domain);
+ if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())
+ its_init(handle, &gic_data.rdists, gic_data.domain);
gic_smp_init();
gic_dist_init();
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 390fac5..58e5b4e 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -91,7 +91,27 @@ struct gic_chip_data {
#endif
};
-static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+#ifdef CONFIG_BL_SWITCHER
+
+static DEFINE_RAW_SPINLOCK(cpu_map_lock);
+
+#define gic_lock_irqsave(f) \
+ raw_spin_lock_irqsave(&cpu_map_lock, (f))
+#define gic_unlock_irqrestore(f) \
+ raw_spin_unlock_irqrestore(&cpu_map_lock, (f))
+
+#define gic_lock() raw_spin_lock(&cpu_map_lock)
+#define gic_unlock() raw_spin_unlock(&cpu_map_lock)
+
+#else
+
+#define gic_lock_irqsave(f) do { (void)(f); } while(0)
+#define gic_unlock_irqrestore(f) do { (void)(f); } while(0)
+
+#define gic_lock() do { } while(0)
+#define gic_unlock() do { } while(0)
+
+#endif
/*
* The GIC mapping of CPU interfaces does not necessarily match
@@ -317,12 +337,12 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
return -EINVAL;
- raw_spin_lock_irqsave(&irq_controller_lock, flags);
+ gic_lock_irqsave(flags);
mask = 0xff << shift;
bit = gic_cpu_map[cpu] << shift;
val = readl_relaxed(reg) & ~mask;
writel_relaxed(val | bit, reg);
- raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
+ gic_unlock_irqrestore(flags);
return IRQ_SET_MASK_OK_DONE;
}
@@ -374,9 +394,7 @@ static void gic_handle_cascade_irq(struct irq_desc *desc)
chained_irq_enter(chip, desc);
- raw_spin_lock(&irq_controller_lock);
status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK);
- raw_spin_unlock(&irq_controller_lock);
gic_irq = (status & GICC_IAR_INT_ID_MASK);
if (gic_irq == GICC_INT_SPURIOUS)
@@ -776,7 +794,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
return;
}
- raw_spin_lock_irqsave(&irq_controller_lock, flags);
+ gic_lock_irqsave(flags);
/* Convert our logical CPU mask into a physical one. */
for_each_cpu(cpu, mask)
@@ -791,7 +809,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
/* this always happens on GIC0 */
writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT);
- raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
+ gic_unlock_irqrestore(flags);
}
#endif
@@ -859,7 +877,7 @@ void gic_migrate_target(unsigned int new_cpu_id)
cur_target_mask = 0x01010101 << cur_cpu_id;
ror_val = (cur_cpu_id - new_cpu_id) & 31;
- raw_spin_lock(&irq_controller_lock);
+ gic_lock();
/* Update the target interface for this logical CPU */
gic_cpu_map[cpu] = 1 << new_cpu_id;
@@ -879,7 +897,7 @@ void gic_migrate_target(unsigned int new_cpu_id)
}
}
- raw_spin_unlock(&irq_controller_lock);
+ gic_unlock();
/*
* Now let's migrate and clear any potential SGIs that might be
@@ -921,7 +939,7 @@ unsigned long gic_get_sgir_physaddr(void)
return gic_dist_physaddr + GIC_DIST_SOFTINT;
}
-void __init gic_init_physaddr(struct device_node *node)
+static void __init gic_init_physaddr(struct device_node *node)
{
struct resource res;
if (of_address_to_resource(node, 0, &res) == 0) {
diff --git a/drivers/irqchip/irq-jcore-aic.c b/drivers/irqchip/irq-jcore-aic.c
new file mode 100644
index 0000000..84b01de
--- /dev/null
+++ b/drivers/irqchip/irq-jcore-aic.c
@@ -0,0 +1,95 @@
+/*
+ * J-Core SoC AIC driver
+ *
+ * Copyright (C) 2015-2016 Smart Energy Instruments, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define JCORE_AIC_MAX_HWIRQ 127
+#define JCORE_AIC1_MIN_HWIRQ 16
+#define JCORE_AIC2_MIN_HWIRQ 64
+
+#define JCORE_AIC1_INTPRI_REG 8
+
+static struct irq_chip jcore_aic;
+
+static int jcore_aic_irqdomain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ struct irq_chip *aic = d->host_data;
+
+ irq_set_chip_and_handler(irq, aic, handle_simple_irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops jcore_aic_irqdomain_ops = {
+ .map = jcore_aic_irqdomain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static void noop(struct irq_data *data)
+{
+}
+
+static int __init aic_irq_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ unsigned min_irq = JCORE_AIC2_MIN_HWIRQ;
+ unsigned dom_sz = JCORE_AIC_MAX_HWIRQ+1;
+ struct irq_domain *domain;
+
+ pr_info("Initializing J-Core AIC\n");
+
+ /* AIC1 needs priority initialization to receive interrupts. */
+ if (of_device_is_compatible(node, "jcore,aic1")) {
+ unsigned cpu;
+
+ for_each_present_cpu(cpu) {
+ void __iomem *base = of_iomap(node, cpu);
+
+ if (!base) {
+ pr_err("Unable to map AIC for cpu %u\n", cpu);
+ return -ENOMEM;
+ }
+ __raw_writel(0xffffffff, base + JCORE_AIC1_INTPRI_REG);
+ iounmap(base);
+ }
+ min_irq = JCORE_AIC1_MIN_HWIRQ;
+ }
+
+ /*
+ * The irq chip framework requires either mask/unmask or enable/disable
+ * function pointers to be provided, but the hardware does not have any
+ * such mechanism; the only interrupt masking is at the cpu level and
+ * it affects all interrupts. We provide dummy mask/unmask. The hardware
+ * handles all interrupt control and clears pending status when the cpu
+ * accepts the interrupt.
+ */
+ jcore_aic.irq_mask = noop;
+ jcore_aic.irq_unmask = noop;
+ jcore_aic.name = "AIC";
+
+ domain = irq_domain_add_linear(node, dom_sz, &jcore_aic_irqdomain_ops,
+ &jcore_aic);
+ if (!domain)
+ return -ENOMEM;
+ irq_create_strict_mappings(domain, min_irq, min_irq, dom_sz - min_irq);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(jcore_aic2, "jcore,aic2", aic_irq_of_init);
+IRQCHIP_DECLARE(jcore_aic1, "jcore,aic1", aic_irq_of_init);
diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c
index deb89d6..54a5e87 100644
--- a/drivers/irqchip/irq-keystone.c
+++ b/drivers/irqchip/irq-keystone.c
@@ -109,7 +109,7 @@ static void keystone_irq_handler(struct irq_desc *desc)
dev_dbg(kirq->dev, "dispatch bit %d, virq %d\n",
src, virq);
if (!virq)
- dev_warn(kirq->dev, "sporious irq detected hwirq %d, virq %d\n",
+ dev_warn(kirq->dev, "spurious irq detected hwirq %d, virq %d\n",
src, virq);
generic_handle_irq(virq);
}
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index 61856964..c0178a1 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -371,18 +371,13 @@ static void gic_handle_shared_int(bool chained)
bitmap_and(pending, pending, intrmask, gic_shared_intrs);
bitmap_and(pending, pending, pcpu_mask, gic_shared_intrs);
- intr = find_first_bit(pending, gic_shared_intrs);
- while (intr != gic_shared_intrs) {
+ for_each_set_bit(intr, pending, gic_shared_intrs) {
virq = irq_linear_revmap(gic_irq_domain,
GIC_SHARED_TO_HWIRQ(intr));
if (chained)
generic_handle_irq(virq);
else
do_IRQ(virq);
-
- /* go to next pending bit */
- bitmap_clear(pending, intr, 1);
- intr = find_first_bit(pending, gic_shared_intrs);
}
}
@@ -518,18 +513,13 @@ static void gic_handle_local_int(bool chained)
bitmap_and(&pending, &pending, &masked, GIC_NUM_LOCAL_INTRS);
- intr = find_first_bit(&pending, GIC_NUM_LOCAL_INTRS);
- while (intr != GIC_NUM_LOCAL_INTRS) {
+ for_each_set_bit(intr, &pending, GIC_NUM_LOCAL_INTRS) {
virq = irq_linear_revmap(gic_irq_domain,
GIC_LOCAL_TO_HWIRQ(intr));
if (chained)
generic_handle_irq(virq);
else
do_IRQ(virq);
-
- /* go to next pending bit */
- bitmap_clear(&pending, intr, 1);
- intr = find_first_bit(&pending, GIC_NUM_LOCAL_INTRS);
}
}
diff --git a/drivers/irqchip/irq-mvebu-pic.c b/drivers/irqchip/irq-mvebu-pic.c
new file mode 100644
index 0000000..eec6395
--- /dev/null
+++ b/drivers/irqchip/irq-mvebu-pic.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 Marvell
+ *
+ * Yehuda Yitschak <yehuday@marvell.com>
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#define PIC_CAUSE 0x0
+#define PIC_MASK 0x4
+
+#define PIC_MAX_IRQS 32
+#define PIC_MAX_IRQ_MASK ((1UL << PIC_MAX_IRQS) - 1)
+
+struct mvebu_pic {
+ void __iomem *base;
+ u32 parent_irq;
+ struct irq_domain *domain;
+ struct irq_chip irq_chip;
+};
+
+static void mvebu_pic_reset(struct mvebu_pic *pic)
+{
+ /* ACK and mask all interrupts */
+ writel(0, pic->base + PIC_MASK);
+ writel(PIC_MAX_IRQ_MASK, pic->base + PIC_CAUSE);
+}
+
+static void mvebu_pic_eoi_irq(struct irq_data *d)
+{
+ struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
+
+ writel(1 << d->hwirq, pic->base + PIC_CAUSE);
+}
+
+static void mvebu_pic_mask_irq(struct irq_data *d)
+{
+ struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
+ u32 reg;
+
+ reg = readl(pic->base + PIC_MASK);
+ reg |= (1 << d->hwirq);
+ writel(reg, pic->base + PIC_MASK);
+}
+
+static void mvebu_pic_unmask_irq(struct irq_data *d)
+{
+ struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
+ u32 reg;
+
+ reg = readl(pic->base + PIC_MASK);
+ reg &= ~(1 << d->hwirq);
+ writel(reg, pic->base + PIC_MASK);
+}
+
+static int mvebu_pic_irq_map(struct irq_domain *domain, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct mvebu_pic *pic = domain->host_data;
+
+ irq_set_percpu_devid(virq);
+ irq_set_chip_data(virq, pic);
+ irq_set_chip_and_handler(virq, &pic->irq_chip,
+ handle_percpu_devid_irq);
+ irq_set_status_flags(virq, IRQ_LEVEL);
+ irq_set_probe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops mvebu_pic_domain_ops = {
+ .map = mvebu_pic_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static void mvebu_pic_handle_cascade_irq(struct irq_desc *desc)
+{
+ struct mvebu_pic *pic = irq_desc_get_handler_data(desc);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned long irqmap, irqn;
+ unsigned int cascade_irq;
+
+ irqmap = readl_relaxed(pic->base + PIC_CAUSE);
+ chained_irq_enter(chip, desc);
+
+ for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
+ cascade_irq = irq_find_mapping(pic->domain, irqn);
+ generic_handle_irq(cascade_irq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static void mvebu_pic_enable_percpu_irq(void *data)
+{
+ struct mvebu_pic *pic = data;
+
+ mvebu_pic_reset(pic);
+ enable_percpu_irq(pic->parent_irq, IRQ_TYPE_NONE);
+}
+
+static void mvebu_pic_disable_percpu_irq(void *data)
+{
+ struct mvebu_pic *pic = data;
+
+ disable_percpu_irq(pic->parent_irq);
+}
+
+static int mvebu_pic_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct mvebu_pic *pic;
+ struct irq_chip *irq_chip;
+ struct resource *res;
+
+ pic = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pic), GFP_KERNEL);
+ if (!pic)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pic->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pic->base))
+ return PTR_ERR(pic->base);
+
+ irq_chip = &pic->irq_chip;
+ irq_chip->name = dev_name(&pdev->dev);
+ irq_chip->irq_mask = mvebu_pic_mask_irq;
+ irq_chip->irq_unmask = mvebu_pic_unmask_irq;
+ irq_chip->irq_eoi = mvebu_pic_eoi_irq;
+
+ pic->parent_irq = irq_of_parse_and_map(node, 0);
+ if (pic->parent_irq <= 0) {
+ dev_err(&pdev->dev, "Failed to parse parent interrupt\n");
+ return -EINVAL;
+ }
+
+ pic->domain = irq_domain_add_linear(node, PIC_MAX_IRQS,
+ &mvebu_pic_domain_ops, pic);
+ if (!pic->domain) {
+ dev_err(&pdev->dev, "Failed to allocate irq domain\n");
+ return -ENOMEM;
+ }
+
+ irq_set_chained_handler(pic->parent_irq, mvebu_pic_handle_cascade_irq);
+ irq_set_handler_data(pic->parent_irq, pic);
+
+ on_each_cpu(mvebu_pic_enable_percpu_irq, pic, 1);
+
+ platform_set_drvdata(pdev, pic);
+
+ return 0;
+}
+
+static int mvebu_pic_remove(struct platform_device *pdev)
+{
+ struct mvebu_pic *pic = platform_get_drvdata(pdev);
+
+ on_each_cpu(mvebu_pic_disable_percpu_irq, pic, 1);
+ irq_domain_remove(pic->domain);
+
+ return 0;
+}
+
+static const struct of_device_id mvebu_pic_of_match[] = {
+ { .compatible = "marvell,armada-8k-pic", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mvebu_pic_of_match);
+
+static struct platform_driver mvebu_pic_driver = {
+ .probe = mvebu_pic_probe,
+ .remove = mvebu_pic_remove,
+ .driver = {
+ .name = "mvebu-pic",
+ .of_match_table = mvebu_pic_of_match,
+ },
+};
+module_platform_driver(mvebu_pic_driver);
+
+MODULE_AUTHOR("Yehuda Yitschak <yehuday@marvell.com>");
+MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mvebu_pic");
+
diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c
new file mode 100644
index 0000000..491568c
--- /dev/null
+++ b/drivers/irqchip/irq-stm32-exti.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) Maxime Coquelin 2015
+ * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define EXTI_IMR 0x0
+#define EXTI_EMR 0x4
+#define EXTI_RTSR 0x8
+#define EXTI_FTSR 0xc
+#define EXTI_SWIER 0x10
+#define EXTI_PR 0x14
+
+static void stm32_irq_handler(struct irq_desc *desc)
+{
+ struct irq_domain *domain = irq_desc_get_handler_data(desc);
+ struct irq_chip_generic *gc = domain->gc->gc[0];
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ unsigned long pending;
+ int n;
+
+ chained_irq_enter(chip, desc);
+
+ while ((pending = irq_reg_readl(gc, EXTI_PR))) {
+ for_each_set_bit(n, &pending, BITS_PER_LONG) {
+ generic_handle_irq(irq_find_mapping(domain, n));
+ irq_reg_writel(gc, BIT(n), EXTI_PR);
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static int stm32_irq_set_type(struct irq_data *data, unsigned int type)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ int pin = data->hwirq;
+ u32 rtsr, ftsr;
+
+ irq_gc_lock(gc);
+
+ rtsr = irq_reg_readl(gc, EXTI_RTSR);
+ ftsr = irq_reg_readl(gc, EXTI_FTSR);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ rtsr |= BIT(pin);
+ ftsr &= ~BIT(pin);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ rtsr &= ~BIT(pin);
+ ftsr |= BIT(pin);
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ rtsr |= BIT(pin);
+ ftsr |= BIT(pin);
+ break;
+ default:
+ irq_gc_unlock(gc);
+ return -EINVAL;
+ }
+
+ irq_reg_writel(gc, rtsr, EXTI_RTSR);
+ irq_reg_writel(gc, ftsr, EXTI_FTSR);
+
+ irq_gc_unlock(gc);
+
+ return 0;
+}
+
+static int stm32_irq_set_wake(struct irq_data *data, unsigned int on)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
+ int pin = data->hwirq;
+ u32 emr;
+
+ irq_gc_lock(gc);
+
+ emr = irq_reg_readl(gc, EXTI_EMR);
+ if (on)
+ emr |= BIT(pin);
+ else
+ emr &= ~BIT(pin);
+ irq_reg_writel(gc, emr, EXTI_EMR);
+
+ irq_gc_unlock(gc);
+
+ return 0;
+}
+
+static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct irq_chip_generic *gc = d->gc->gc[0];
+ struct irq_fwspec *fwspec = data;
+ irq_hw_number_t hwirq;
+
+ hwirq = fwspec->param[0];
+
+ irq_map_generic_chip(d, virq, hwirq);
+ irq_domain_set_info(d, virq, hwirq, &gc->chip_types->chip, gc,
+ handle_simple_irq, NULL, NULL);
+
+ return 0;
+}
+
+static void stm32_exti_free(struct irq_domain *d, unsigned int virq,
+ unsigned int nr_irqs)
+{
+ struct irq_data *data = irq_domain_get_irq_data(d, virq);
+
+ irq_domain_reset_irq_data(data);
+}
+
+struct irq_domain_ops irq_exti_domain_ops = {
+ .map = irq_map_generic_chip,
+ .xlate = irq_domain_xlate_onetwocell,
+ .alloc = stm32_exti_alloc,
+ .free = stm32_exti_free,
+};
+
+static int __init stm32_exti_init(struct device_node *node,
+ struct device_node *parent)
+{
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ int nr_irqs, nr_exti, ret, i;
+ struct irq_chip_generic *gc;
+ struct irq_domain *domain;
+ void *base;
+
+ base = of_iomap(node, 0);
+ if (!base) {
+ pr_err("%s: Unable to map registers\n", node->full_name);
+ return -ENOMEM;
+ }
+
+ /* Determine number of irqs supported */
+ writel_relaxed(~0UL, base + EXTI_RTSR);
+ nr_exti = fls(readl_relaxed(base + EXTI_RTSR));
+ writel_relaxed(0, base + EXTI_RTSR);
+
+ pr_info("%s: %d External IRQs detected\n", node->full_name, nr_exti);
+
+ domain = irq_domain_add_linear(node, nr_exti,
+ &irq_exti_domain_ops, NULL);
+ if (!domain) {
+ pr_err("%s: Could not register interrupt domain.\n",
+ node->name);
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ ret = irq_alloc_domain_generic_chips(domain, nr_exti, 1, "exti",
+ handle_edge_irq, clr, 0, 0);
+ if (ret) {
+ pr_err("%s: Could not allocate generic interrupt chip.\n",
+ node->full_name);
+ goto out_free_domain;
+ }
+
+ gc = domain->gc->gc[0];
+ gc->reg_base = base;
+ gc->chip_types->type = IRQ_TYPE_EDGE_BOTH;
+ gc->chip_types->chip.name = gc->chip_types[0].chip.name;
+ gc->chip_types->chip.irq_ack = irq_gc_ack_set_bit;
+ gc->chip_types->chip.irq_mask = irq_gc_mask_clr_bit;
+ gc->chip_types->chip.irq_unmask = irq_gc_mask_set_bit;
+ gc->chip_types->chip.irq_set_type = stm32_irq_set_type;
+ gc->chip_types->chip.irq_set_wake = stm32_irq_set_wake;
+ gc->chip_types->regs.ack = EXTI_PR;
+ gc->chip_types->regs.mask = EXTI_IMR;
+ gc->chip_types->handler = handle_edge_irq;
+
+ nr_irqs = of_irq_count(node);
+ for (i = 0; i < nr_irqs; i++) {
+ unsigned int irq = irq_of_parse_and_map(node, i);
+
+ irq_set_handler_data(irq, domain);
+ irq_set_chained_handler(irq, stm32_irq_handler);
+ }
+
+ return 0;
+
+out_free_domain:
+ irq_domain_remove(domain);
+out_unmap:
+ iounmap(base);
+ return ret;
+}
+
+IRQCHIP_DECLARE(stm32_exti, "st,stm32-exti", stm32_exti_init);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 9dcc9b1..7a628c6 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -584,6 +584,18 @@ config LEDS_SEAD3
This driver can also be built as a module. If so the module
will be called leds-sead3.
+config LEDS_IS31FL319X
+ tristate "LED Support for ISSI IS31FL319x I2C LED controller family"
+ depends on LEDS_CLASS && I2C && OF
+ select REGMAP_I2C
+ help
+ This option enables support for LEDs connected to ISSI IS31FL319x
+ fancy LED driver chips accessed via the I2C bus.
+ Driver supports individual PWM brightness control for each channel.
+
+ This driver can also be built as a module. If so the module will be
+ called leds-is31fl319x.
+
config LEDS_IS31FL32XX
tristate "LED support for ISSI IS31FL32XX I2C LED controller family"
depends on LEDS_CLASS && I2C && OF
@@ -631,6 +643,22 @@ config LEDS_VERSATILE
This option enabled support for the LEDs on the ARM Versatile
and RealView boards. Say Y to enabled these.
+config LEDS_PM8058
+ tristate "LED Support for the Qualcomm PM8058 PMIC"
+ depends on MFD_PM8921_CORE
+ depends on LEDS_CLASS
+ help
+ Choose this option if you want to use the LED drivers in
+ the Qualcomm PM8058 PMIC.
+
+config LEDS_MLXCPLD
+ tristate "LED support for the Mellanox boards"
+ depends on X86_64 && DMI
+ depends on LEDS_CLASS
+ help
+ This option enabled support for the LEDs on the Mellanox
+ boards. Say Y to enabled these.
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 0684c86..3965070 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -67,7 +67,10 @@ obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o
obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o
obj-$(CONFIG_LEDS_SEAD3) += leds-sead3.o
+obj-$(CONFIG_LEDS_IS31FL319X) += leds-is31fl319x.o
obj-$(CONFIG_LEDS_IS31FL32XX) += leds-is31fl32xx.o
+obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
+obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index c92702a..431123b 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -11,7 +11,7 @@
*
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/spinlock.h>
@@ -81,21 +81,23 @@ ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
down_read(&led_cdev->trigger_lock);
if (!led_cdev->trigger)
- len += sprintf(buf+len, "[none] ");
+ len += scnprintf(buf+len, PAGE_SIZE - len, "[none] ");
else
- len += sprintf(buf+len, "none ");
+ len += scnprintf(buf+len, PAGE_SIZE - len, "none ");
list_for_each_entry(trig, &trigger_list, next_trig) {
if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
trig->name))
- len += sprintf(buf+len, "[%s] ", trig->name);
+ len += scnprintf(buf+len, PAGE_SIZE - len, "[%s] ",
+ trig->name);
else
- len += sprintf(buf+len, "%s ", trig->name);
+ len += scnprintf(buf+len, PAGE_SIZE - len, "%s ",
+ trig->name);
}
up_read(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
- len += sprintf(len+buf, "\n");
+ len += scnprintf(len+buf, PAGE_SIZE - len, "\n");
return len;
}
EXPORT_SYMBOL_GPL(led_trigger_show);
@@ -108,6 +110,9 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
char *envp[2];
const char *name;
+ if (!led_cdev->trigger && !trig)
+ return;
+
name = trig ? trig->name : "none";
event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
@@ -136,7 +141,9 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
if (event) {
envp[0] = event;
envp[1] = NULL;
- kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp);
+ if (kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp))
+ dev_err(led_cdev->dev,
+ "%s: Error sending uevent\n", __func__);
kfree(event);
}
}
@@ -357,7 +364,3 @@ void led_trigger_unregister_simple(struct led_trigger *trig)
kfree(trig);
}
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
-
-MODULE_AUTHOR("Richard Purdie");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("LED Triggers Core");
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 9b991d4..d400dca 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -26,15 +26,19 @@ struct gpio_led_data {
struct gpio_desc *gpiod;
u8 can_sleep;
u8 blinking;
- int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state,
- unsigned long *delay_on, unsigned long *delay_off);
+ gpio_blink_set_t platform_gpio_blink_set;
};
+static inline struct gpio_led_data *
+ cdev_to_gpio_led_data(struct led_classdev *led_cdev)
+{
+ return container_of(led_cdev, struct gpio_led_data, cdev);
+}
+
static void gpio_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- struct gpio_led_data *led_dat =
- container_of(led_cdev, struct gpio_led_data, cdev);
+ struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev);
int level;
if (value == LED_OFF)
@@ -64,8 +68,7 @@ static int gpio_led_set_blocking(struct led_classdev *led_cdev,
static int gpio_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
- struct gpio_led_data *led_dat =
- container_of(led_cdev, struct gpio_led_data, cdev);
+ struct gpio_led_data *led_dat = cdev_to_gpio_led_data(led_cdev);
led_dat->blinking = 1;
return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK,
@@ -74,8 +77,7 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
static int create_gpio_led(const struct gpio_led *template,
struct gpio_led_data *led_dat, struct device *parent,
- int (*blink_set)(struct gpio_desc *, int, unsigned long *,
- unsigned long *))
+ gpio_blink_set_t blink_set)
{
int ret, state;
@@ -120,10 +122,13 @@ static int create_gpio_led(const struct gpio_led *template,
led_dat->platform_gpio_blink_set = blink_set;
led_dat->cdev.blink_set = gpio_blink_set;
}
- if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
- state = !!gpiod_get_value_cansleep(led_dat->gpiod);
- else
+ if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) {
+ state = gpiod_get_value_cansleep(led_dat->gpiod);
+ if (state < 0)
+ return state;
+ } else {
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
+ }
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
if (!template->retain_state_suspended)
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
@@ -134,7 +139,7 @@ static int create_gpio_led(const struct gpio_led *template,
if (ret < 0)
return ret;
- return led_classdev_register(parent, &led_dat->cdev);
+ return devm_led_classdev_register(parent, &led_dat->cdev);
}
struct gpio_leds_priv {
@@ -154,7 +159,6 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
struct fwnode_handle *child;
struct gpio_leds_priv *priv;
int count, ret;
- struct device_node *np;
count = device_get_child_node_count(dev);
if (!count)
@@ -168,26 +172,22 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];
struct gpio_led led = {};
const char *state = NULL;
+ struct device_node *np = to_of_node(child);
led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
if (IS_ERR(led.gpiod)) {
fwnode_handle_put(child);
- ret = PTR_ERR(led.gpiod);
- goto err;
+ return ERR_CAST(led.gpiod);
}
- np = to_of_node(child);
-
- if (fwnode_property_present(child, "label")) {
- fwnode_property_read_string(child, "label", &led.name);
- } else {
- if (IS_ENABLED(CONFIG_OF) && !led.name && np)
- led.name = np->name;
- if (!led.name) {
- ret = -EINVAL;
- goto err;
- }
+ ret = fwnode_property_read_string(child, "label", &led.name);
+ if (ret && IS_ENABLED(CONFIG_OF) && np)
+ led.name = np->name;
+ if (!led.name) {
+ fwnode_handle_put(child);
+ return ERR_PTR(-EINVAL);
}
+
fwnode_property_read_string(child, "linux,default-trigger",
&led.default_trigger);
@@ -209,18 +209,13 @@ static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
ret = create_gpio_led(&led, led_dat, dev, NULL);
if (ret < 0) {
fwnode_handle_put(child);
- goto err;
+ return ERR_PTR(ret);
}
led_dat->cdev.dev->of_node = np;
priv->num_leds++;
}
return priv;
-
-err:
- for (count = priv->num_leds - 1; count >= 0; count--)
- led_classdev_unregister(&priv->leds[count].cdev);
- return ERR_PTR(ret);
}
static const struct of_device_id of_gpio_leds_match[] = {
@@ -248,13 +243,8 @@ static int gpio_led_probe(struct platform_device *pdev)
ret = create_gpio_led(&pdata->leds[i],
&priv->leds[i],
&pdev->dev, pdata->gpio_blink_set);
- if (ret < 0) {
- /* On failure: unwind the led creations */
- for (i = i - 1; i >= 0; i--)
- led_classdev_unregister(
- &priv->leds[i].cdev);
+ if (ret < 0)
return ret;
- }
}
} else {
priv = gpio_leds_create(pdev);
@@ -267,17 +257,6 @@ static int gpio_led_probe(struct platform_device *pdev)
return 0;
}
-static int gpio_led_remove(struct platform_device *pdev)
-{
- struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
- int i;
-
- for (i = 0; i < priv->num_leds; i++)
- led_classdev_unregister(&priv->leds[i].cdev);
-
- return 0;
-}
-
static void gpio_led_shutdown(struct platform_device *pdev)
{
struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
@@ -292,7 +271,6 @@ static void gpio_led_shutdown(struct platform_device *pdev)
static struct platform_driver gpio_led_driver = {
.probe = gpio_led_probe,
- .remove = gpio_led_remove,
.shutdown = gpio_led_shutdown,
.driver = {
.name = "leds-gpio",
diff --git a/drivers/leds/leds-is31fl319x.c b/drivers/leds/leds-is31fl319x.c
new file mode 100644
index 0000000..f123309
--- /dev/null
+++ b/drivers/leds/leds-is31fl319x.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2015-16 Golden Delicious Computers
+ *
+ * Author: Nikolaus Schaller <hns@goldelico.com>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * LED driver for the IS31FL319{0,1,3,6,9} to drive 1, 3, 6 or 9 light
+ * effect LEDs.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* register numbers */
+#define IS31FL319X_SHUTDOWN 0x00
+#define IS31FL319X_CTRL1 0x01
+#define IS31FL319X_CTRL2 0x02
+#define IS31FL319X_CONFIG1 0x03
+#define IS31FL319X_CONFIG2 0x04
+#define IS31FL319X_RAMP_MODE 0x05
+#define IS31FL319X_BREATH_MASK 0x06
+#define IS31FL319X_PWM(channel) (0x07 + channel)
+#define IS31FL319X_DATA_UPDATE 0x10
+#define IS31FL319X_T0(channel) (0x11 + channel)
+#define IS31FL319X_T123_1 0x1a
+#define IS31FL319X_T123_2 0x1b
+#define IS31FL319X_T123_3 0x1c
+#define IS31FL319X_T4(channel) (0x1d + channel)
+#define IS31FL319X_TIME_UPDATE 0x26
+#define IS31FL319X_RESET 0xff
+
+#define IS31FL319X_REG_CNT (IS31FL319X_RESET + 1)
+
+#define IS31FL319X_MAX_LEDS 9
+
+/* CS (Current Setting) in CONFIG2 register */
+#define IS31FL319X_CONFIG2_CS_SHIFT 4
+#define IS31FL319X_CONFIG2_CS_MASK 0x7
+#define IS31FL319X_CONFIG2_CS_STEP_REF 12
+
+#define IS31FL319X_CURRENT_MIN ((u32)5000)
+#define IS31FL319X_CURRENT_MAX ((u32)40000)
+#define IS31FL319X_CURRENT_STEP ((u32)5000)
+#define IS31FL319X_CURRENT_DEFAULT ((u32)20000)
+
+/* Audio gain in CONFIG2 register */
+#define IS31FL319X_AUDIO_GAIN_DB_MAX ((u32)21)
+#define IS31FL319X_AUDIO_GAIN_DB_STEP ((u32)3)
+
+/*
+ * regmap is used as a cache of chip's register space,
+ * to avoid reading back brightness values from chip,
+ * which is known to hang.
+ */
+struct is31fl319x_chip {
+ const struct is31fl319x_chipdef *cdef;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct mutex lock;
+ u32 audio_gain_db;
+
+ struct is31fl319x_led {
+ struct is31fl319x_chip *chip;
+ struct led_classdev cdev;
+ u32 max_microamp;
+ bool configured;
+ } leds[IS31FL319X_MAX_LEDS];
+};
+
+struct is31fl319x_chipdef {
+ int num_leds;
+};
+
+static const struct is31fl319x_chipdef is31fl3190_cdef = {
+ .num_leds = 1,
+};
+
+static const struct is31fl319x_chipdef is31fl3193_cdef = {
+ .num_leds = 3,
+};
+
+static const struct is31fl319x_chipdef is31fl3196_cdef = {
+ .num_leds = 6,
+};
+
+static const struct is31fl319x_chipdef is31fl3199_cdef = {
+ .num_leds = 9,
+};
+
+static const struct of_device_id of_is31fl319x_match[] = {
+ { .compatible = "issi,is31fl3190", .data = &is31fl3190_cdef, },
+ { .compatible = "issi,is31fl3191", .data = &is31fl3190_cdef, },
+ { .compatible = "issi,is31fl3193", .data = &is31fl3193_cdef, },
+ { .compatible = "issi,is31fl3196", .data = &is31fl3196_cdef, },
+ { .compatible = "issi,is31fl3199", .data = &is31fl3199_cdef, },
+ { .compatible = "si-en,sn3199", .data = &is31fl3199_cdef, },
+ { }
+};
+MODULE_DEVICE_TABLE(of, of_is31fl319x_match);
+
+static int is31fl319x_brightness_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct is31fl319x_led *led = container_of(cdev, struct is31fl319x_led,
+ cdev);
+ struct is31fl319x_chip *is31 = led->chip;
+ int chan = led - is31->leds;
+ int ret;
+ int i;
+ u8 ctrl1 = 0, ctrl2 = 0;
+
+ dev_dbg(&is31->client->dev, "%s %d: %d\n", __func__, chan, brightness);
+
+ mutex_lock(&is31->lock);
+
+ /* update PWM register */
+ ret = regmap_write(is31->regmap, IS31FL319X_PWM(chan), brightness);
+ if (ret < 0)
+ goto out;
+
+ /* read current brightness of all PWM channels */
+ for (i = 0; i < is31->cdef->num_leds; i++) {
+ unsigned int pwm_value;
+ bool on;
+
+ /*
+ * since neither cdev nor the chip can provide
+ * the current setting, we read from the regmap cache
+ */
+
+ ret = regmap_read(is31->regmap, IS31FL319X_PWM(i), &pwm_value);
+ dev_dbg(&is31->client->dev, "%s read %d: ret=%d: %d\n",
+ __func__, i, ret, pwm_value);
+ on = ret >= 0 && pwm_value > LED_OFF;
+
+ if (i < 3)
+ ctrl1 |= on << i; /* 0..2 => bit 0..2 */
+ else if (i < 6)
+ ctrl1 |= on << (i + 1); /* 3..5 => bit 4..6 */
+ else
+ ctrl2 |= on << (i - 6); /* 6..8 => bit 0..2 */
+ }
+
+ if (ctrl1 > 0 || ctrl2 > 0) {
+ dev_dbg(&is31->client->dev, "power up %02x %02x\n",
+ ctrl1, ctrl2);
+ regmap_write(is31->regmap, IS31FL319X_CTRL1, ctrl1);
+ regmap_write(is31->regmap, IS31FL319X_CTRL2, ctrl2);
+ /* update PWMs */
+ regmap_write(is31->regmap, IS31FL319X_DATA_UPDATE, 0x00);
+ /* enable chip from shut down */
+ ret = regmap_write(is31->regmap, IS31FL319X_SHUTDOWN, 0x01);
+ } else {
+ dev_dbg(&is31->client->dev, "power down\n");
+ /* shut down (no need to clear CTRL1/2) */
+ ret = regmap_write(is31->regmap, IS31FL319X_SHUTDOWN, 0x00);
+ }
+
+out:
+ mutex_unlock(&is31->lock);
+
+ return ret;
+}
+
+static int is31fl319x_parse_child_dt(const struct device *dev,
+ const struct device_node *child,
+ struct is31fl319x_led *led)
+{
+ struct led_classdev *cdev = &led->cdev;
+ int ret;
+
+ if (of_property_read_string(child, "label", &cdev->name))
+ cdev->name = child->name;
+
+ ret = of_property_read_string(child, "linux,default-trigger",
+ &cdev->default_trigger);
+ if (ret < 0 && ret != -EINVAL) /* is optional */
+ return ret;
+
+ led->max_microamp = IS31FL319X_CURRENT_DEFAULT;
+ ret = of_property_read_u32(child, "led-max-microamp",
+ &led->max_microamp);
+ if (!ret) {
+ if (led->max_microamp < IS31FL319X_CURRENT_MIN)
+ return -EINVAL; /* not supported */
+ led->max_microamp = min(led->max_microamp,
+ IS31FL319X_CURRENT_MAX);
+ }
+
+ return 0;
+}
+
+static int is31fl319x_parse_dt(struct device *dev,
+ struct is31fl319x_chip *is31)
+{
+ struct device_node *np = dev->of_node, *child;
+ const struct of_device_id *of_dev_id;
+ int count;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ of_dev_id = of_match_device(of_is31fl319x_match, dev);
+ if (!of_dev_id) {
+ dev_err(dev, "Failed to match device with supported chips\n");
+ return -EINVAL;
+ }
+
+ is31->cdef = of_dev_id->data;
+
+ count = of_get_child_count(np);
+
+ dev_dbg(dev, "probe %s with %d leds defined in DT\n",
+ of_dev_id->compatible, count);
+
+ if (!count || count > is31->cdef->num_leds) {
+ dev_err(dev, "Number of leds defined must be between 1 and %u\n",
+ is31->cdef->num_leds);
+ return -ENODEV;
+ }
+
+ for_each_child_of_node(np, child) {
+ struct is31fl319x_led *led;
+ u32 reg;
+
+ ret = of_property_read_u32(child, "reg", &reg);
+ if (ret) {
+ dev_err(dev, "Failed to read led 'reg' property\n");
+ goto put_child_node;
+ }
+
+ if (reg < 1 || reg > is31->cdef->num_leds) {
+ dev_err(dev, "invalid led reg %u\n", reg);
+ ret = -EINVAL;
+ goto put_child_node;
+ }
+
+ led = &is31->leds[reg - 1];
+
+ if (led->configured) {
+ dev_err(dev, "led %u is already configured\n", reg);
+ ret = -EINVAL;
+ goto put_child_node;
+ }
+
+ ret = is31fl319x_parse_child_dt(dev, child, led);
+ if (ret) {
+ dev_err(dev, "led %u DT parsing failed\n", reg);
+ goto put_child_node;
+ }
+
+ led->configured = true;
+ }
+
+ is31->audio_gain_db = 0;
+ ret = of_property_read_u32(np, "audio-gain-db", &is31->audio_gain_db);
+ if (!ret)
+ is31->audio_gain_db = min(is31->audio_gain_db,
+ IS31FL319X_AUDIO_GAIN_DB_MAX);
+
+ return 0;
+
+put_child_node:
+ of_node_put(child);
+ return ret;
+}
+
+static bool is31fl319x_readable_reg(struct device *dev, unsigned int reg)
+{ /* we have no readable registers */
+ return false;
+}
+
+static bool is31fl319x_volatile_reg(struct device *dev, unsigned int reg)
+{ /* volatile registers are not cached */
+ switch (reg) {
+ case IS31FL319X_DATA_UPDATE:
+ case IS31FL319X_TIME_UPDATE:
+ case IS31FL319X_RESET:
+ return true; /* always write-through */
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default is31fl319x_reg_defaults[] = {
+ { IS31FL319X_CONFIG1, 0x00},
+ { IS31FL319X_CONFIG2, 0x00},
+ { IS31FL319X_PWM(0), 0x00},
+ { IS31FL319X_PWM(1), 0x00},
+ { IS31FL319X_PWM(2), 0x00},
+ { IS31FL319X_PWM(3), 0x00},
+ { IS31FL319X_PWM(4), 0x00},
+ { IS31FL319X_PWM(5), 0x00},
+ { IS31FL319X_PWM(6), 0x00},
+ { IS31FL319X_PWM(7), 0x00},
+ { IS31FL319X_PWM(8), 0x00},
+};
+
+static struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = IS31FL319X_REG_CNT,
+ .cache_type = REGCACHE_FLAT,
+ .readable_reg = is31fl319x_readable_reg,
+ .volatile_reg = is31fl319x_volatile_reg,
+ .reg_defaults = is31fl319x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(is31fl319x_reg_defaults),
+};
+
+static inline int is31fl319x_microamp_to_cs(struct device *dev, u32 microamp)
+{ /* round down to nearest supported value (range check done by caller) */
+ u32 step = microamp / IS31FL319X_CURRENT_STEP;
+
+ return ((IS31FL319X_CONFIG2_CS_STEP_REF - step) &
+ IS31FL319X_CONFIG2_CS_MASK) <<
+ IS31FL319X_CONFIG2_CS_SHIFT; /* CS encoding */
+}
+
+static inline int is31fl319x_db_to_gain(u32 dezibel)
+{ /* round down to nearest supported value (range check done by caller) */
+ return dezibel / IS31FL319X_AUDIO_GAIN_DB_STEP;
+}
+
+static int is31fl319x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct is31fl319x_chip *is31;
+ struct device *dev = &client->dev;
+ struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
+ int err;
+ int i = 0;
+ u32 aggregated_led_microamp = IS31FL319X_CURRENT_MAX;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -EIO;
+
+ is31 = devm_kzalloc(&client->dev, sizeof(*is31), GFP_KERNEL);
+ if (!is31)
+ return -ENOMEM;
+
+ mutex_init(&is31->lock);
+
+ err = is31fl319x_parse_dt(&client->dev, is31);
+ if (err)
+ goto free_mutex;
+
+ is31->client = client;
+ is31->regmap = devm_regmap_init_i2c(client, &regmap_config);
+ if (IS_ERR(is31->regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ err = PTR_ERR(is31->regmap);
+ goto free_mutex;
+ }
+
+ i2c_set_clientdata(client, is31);
+
+ /* check for write-reply from chip (we can't read any registers) */
+ err = regmap_write(is31->regmap, IS31FL319X_RESET, 0x00);
+ if (err < 0) {
+ dev_err(&client->dev, "no response from chip write: err = %d\n",
+ err);
+ err = -EIO; /* does not answer */
+ goto free_mutex;
+ }
+
+ /*
+ * Kernel conventions require per-LED led-max-microamp property.
+ * But the chip does not allow to limit individual LEDs.
+ * So we take minimum from all subnodes for safety of hardware.
+ */
+ for (i = 0; i < is31->cdef->num_leds; i++)
+ if (is31->leds[i].configured &&
+ is31->leds[i].max_microamp < aggregated_led_microamp)
+ aggregated_led_microamp = is31->leds[i].max_microamp;
+
+ regmap_write(is31->regmap, IS31FL319X_CONFIG2,
+ is31fl319x_microamp_to_cs(dev, aggregated_led_microamp) |
+ is31fl319x_db_to_gain(is31->audio_gain_db));
+
+ for (i = 0; i < is31->cdef->num_leds; i++) {
+ struct is31fl319x_led *led = &is31->leds[i];
+
+ if (!led->configured)
+ continue;
+
+ led->chip = is31;
+ led->cdev.brightness_set_blocking = is31fl319x_brightness_set;
+
+ err = devm_led_classdev_register(&client->dev, &led->cdev);
+ if (err < 0)
+ goto free_mutex;
+ }
+
+ return 0;
+
+free_mutex:
+ mutex_destroy(&is31->lock);
+ return err;
+}
+
+static int is31fl319x_remove(struct i2c_client *client)
+{
+ struct is31fl319x_chip *is31 = i2c_get_clientdata(client);
+
+ mutex_destroy(&is31->lock);
+ return 0;
+}
+
+/*
+ * i2c-core (and modalias) requires that id_table be properly filled,
+ * even though it is not used for DeviceTree based instantiation.
+ */
+static const struct i2c_device_id is31fl319x_id[] = {
+ { "is31fl3190" },
+ { "is31fl3191" },
+ { "is31fl3193" },
+ { "is31fl3196" },
+ { "is31fl3199" },
+ { "sn3199" },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, is31fl319x_id);
+
+static struct i2c_driver is31fl319x_driver = {
+ .driver = {
+ .name = "leds-is31fl319x",
+ .of_match_table = of_match_ptr(of_is31fl319x_match),
+ },
+ .probe = is31fl319x_probe,
+ .remove = is31fl319x_remove,
+ .id_table = is31fl319x_id,
+};
+
+module_i2c_driver(is31fl319x_driver);
+
+MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
+MODULE_AUTHOR("Andrey Utkin <andrey_utkin@fastmail.com>");
+MODULE_DESCRIPTION("IS31FL319X LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-mlxcpld.c b/drivers/leds/leds-mlxcpld.c
new file mode 100644
index 0000000..197ab9b
--- /dev/null
+++ b/drivers/leds/leds-mlxcpld.c
@@ -0,0 +1,430 @@
+/*
+ * drivers/leds/leds-mlxcpld.c
+ * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
+ * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/io.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 /* LPC bus access */
+
+/* Color codes for LEDs */
+#define MLXCPLD_LED_OFFSET_HALF 0x01 /* Offset from solid: 3Hz blink */
+#define MLXCPLD_LED_OFFSET_FULL 0x02 /* Offset from solid: 6Hz blink */
+#define MLXCPLD_LED_IS_OFF 0x00 /* Off */
+#define MLXCPLD_LED_RED_STATIC_ON 0x05 /* Solid red */
+#define MLXCPLD_LED_RED_BLINK_HALF (MLXCPLD_LED_RED_STATIC_ON + \
+ MLXCPLD_LED_OFFSET_HALF)
+#define MLXCPLD_LED_RED_BLINK_FULL (MLXCPLD_LED_RED_STATIC_ON + \
+ MLXCPLD_LED_OFFSET_FULL)
+#define MLXCPLD_LED_GREEN_STATIC_ON 0x0D /* Solid green */
+#define MLXCPLD_LED_GREEN_BLINK_HALF (MLXCPLD_LED_GREEN_STATIC_ON + \
+ MLXCPLD_LED_OFFSET_HALF)
+#define MLXCPLD_LED_GREEN_BLINK_FULL (MLXCPLD_LED_GREEN_STATIC_ON + \
+ MLXCPLD_LED_OFFSET_FULL)
+#define MLXCPLD_LED_BLINK_3HZ 167 /* ~167 msec off/on */
+#define MLXCPLD_LED_BLINK_6HZ 83 /* ~83 msec off/on */
+
+/**
+ * mlxcpld_param - LED access parameters:
+ * @offset - offset for LED access in CPLD device
+ * @mask - mask for LED access in CPLD device
+ * @base_color - base color code for LED
+**/
+struct mlxcpld_param {
+ u8 offset;
+ u8 mask;
+ u8 base_color;
+};
+
+/**
+ * mlxcpld_led_priv - LED private data:
+ * @cled - LED class device instance
+ * @param - LED CPLD access parameters
+**/
+struct mlxcpld_led_priv {
+ struct led_classdev cdev;
+ struct mlxcpld_param param;
+};
+
+#define cdev_to_priv(c) container_of(c, struct mlxcpld_led_priv, cdev)
+
+/**
+ * mlxcpld_led_profile - system LED profile (defined per system class):
+ * @offset - offset for LED access in CPLD device
+ * @mask - mask for LED access in CPLD device
+ * @base_color - base color code
+ * @brightness - default brightness setting (on/off)
+ * @name - LED name
+**/
+struct mlxcpld_led_profile {
+ u8 offset;
+ u8 mask;
+ u8 base_color;
+ enum led_brightness brightness;
+ const char *name;
+};
+
+/**
+ * mlxcpld_led_pdata - system LED private data
+ * @pdev - platform device pointer
+ * @pled - LED class device instance
+ * @profile - system configuration profile
+ * @num_led_instances - number of LED instances
+ * @lock - device access lock
+**/
+struct mlxcpld_led_pdata {
+ struct platform_device *pdev;
+ struct mlxcpld_led_priv *pled;
+ struct mlxcpld_led_profile *profile;
+ int num_led_instances;
+ spinlock_t lock;
+};
+
+static struct mlxcpld_led_pdata *mlxcpld_led;
+
+/* Default profile fit the next Mellanox systems:
+ * "msx6710", "msx6720", "msb7700", "msn2700", "msx1410",
+ * "msn2410", "msb7800", "msn2740"
+ */
+static struct mlxcpld_led_profile mlxcpld_led_default_profile[] = {
+ {
+ 0x21, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:fan1:green",
+ },
+ {
+ 0x21, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:fan1:red",
+ },
+ {
+ 0x21, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:fan2:green",
+ },
+ {
+ 0x21, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:fan2:red",
+ },
+ {
+ 0x22, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:fan3:green",
+ },
+ {
+ 0x22, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:fan3:red",
+ },
+ {
+ 0x22, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:fan4:green",
+ },
+ {
+ 0x22, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:fan4:red",
+ },
+ {
+ 0x20, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:psu:green",
+ },
+ {
+ 0x20, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:psu:red",
+ },
+ {
+ 0x20, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:status:green",
+ },
+ {
+ 0x20, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:status:red",
+ },
+};
+
+/* Profile fit the Mellanox systems based on "msn2100" */
+static struct mlxcpld_led_profile mlxcpld_led_msn2100_profile[] = {
+ {
+ 0x21, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:fan:green",
+ },
+ {
+ 0x21, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:fan:red",
+ },
+ {
+ 0x23, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:psu1:green",
+ },
+ {
+ 0x23, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:psu1:red",
+ },
+ {
+ 0x23, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:psu2:green",
+ },
+ {
+ 0x23, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:psu2:red",
+ },
+ {
+ 0x20, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1,
+ "mlxcpld:status:green",
+ },
+ {
+ 0x20, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF,
+ "mlxcpld:status:red",
+ },
+ {
+ 0x24, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, LED_OFF,
+ "mlxcpld:uid:blue",
+ },
+};
+
+enum mlxcpld_led_platform_types {
+ MLXCPLD_LED_PLATFORM_DEFAULT,
+ MLXCPLD_LED_PLATFORM_MSN2100,
+};
+
+static const char *mlx_product_names[] = {
+ "DEFAULT",
+ "MSN2100",
+};
+
+static enum
+mlxcpld_led_platform_types mlxcpld_led_platform_check_sys_type(void)
+{
+ const char *mlx_product_name;
+ int i;
+
+ mlx_product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
+ if (!mlx_product_name)
+ return MLXCPLD_LED_PLATFORM_DEFAULT;
+
+ for (i = 1; i < ARRAY_SIZE(mlx_product_names); i++) {
+ if (strstr(mlx_product_name, mlx_product_names[i]))
+ return i;
+ }
+
+ return MLXCPLD_LED_PLATFORM_DEFAULT;
+}
+
+static void mlxcpld_led_bus_access_func(u16 base, u8 offset, u8 rw_flag,
+ u8 *data)
+{
+ u32 addr = base + offset;
+
+ if (rw_flag == 0)
+ outb(*data, addr);
+ else
+ *data = inb(addr);
+}
+
+static void mlxcpld_led_store_hw(u8 mask, u8 off, u8 vset)
+{
+ u8 nib, val;
+
+ /*
+ * Each LED is controlled through low or high nibble of the relevant
+ * CPLD register. Register offset is specified by off parameter.
+ * Parameter vset provides color code: 0x0 for off, 0x5 for solid red,
+ * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink
+ * green.
+ * Parameter mask specifies which nibble is used for specific LED: mask
+ * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f -
+ * higher nibble (bits from 4 to 7).
+ */
+ spin_lock(&mlxcpld_led->lock);
+ mlxcpld_led_bus_access_func(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, off, 1,
+ &val);
+ nib = (mask == 0xf0) ? vset : (vset << 4);
+ val = (val & mask) | nib;
+ mlxcpld_led_bus_access_func(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, off, 0,
+ &val);
+ spin_unlock(&mlxcpld_led->lock);
+}
+
+static void mlxcpld_led_brightness_set(struct led_classdev *led,
+ enum led_brightness value)
+{
+ struct mlxcpld_led_priv *pled = cdev_to_priv(led);
+
+ if (value) {
+ mlxcpld_led_store_hw(pled->param.mask, pled->param.offset,
+ pled->param.base_color);
+ return;
+ }
+
+ mlxcpld_led_store_hw(pled->param.mask, pled->param.offset,
+ MLXCPLD_LED_IS_OFF);
+}
+
+static int mlxcpld_led_blink_set(struct led_classdev *led,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct mlxcpld_led_priv *pled = cdev_to_priv(led);
+
+ /*
+ * HW supports two types of blinking: full (6Hz) and half (3Hz).
+ * For delay on/off zero default setting 3Hz is used.
+ */
+ if (!(*delay_on == 0 && *delay_off == 0) &&
+ !(*delay_on == MLXCPLD_LED_BLINK_3HZ &&
+ *delay_off == MLXCPLD_LED_BLINK_3HZ) &&
+ !(*delay_on == MLXCPLD_LED_BLINK_6HZ &&
+ *delay_off == MLXCPLD_LED_BLINK_6HZ))
+ return -EINVAL;
+
+ if (*delay_on == MLXCPLD_LED_BLINK_6HZ)
+ mlxcpld_led_store_hw(pled->param.mask, pled->param.offset,
+ pled->param.base_color +
+ MLXCPLD_LED_OFFSET_FULL);
+ else
+ mlxcpld_led_store_hw(pled->param.mask, pled->param.offset,
+ pled->param.base_color +
+ MLXCPLD_LED_OFFSET_HALF);
+
+ return 0;
+}
+
+static int mlxcpld_led_config(struct device *dev,
+ struct mlxcpld_led_pdata *cpld)
+{
+ int i;
+ int err;
+
+ cpld->pled = devm_kzalloc(dev, sizeof(struct mlxcpld_led_priv) *
+ cpld->num_led_instances, GFP_KERNEL);
+ if (!cpld->pled)
+ return -ENOMEM;
+
+ for (i = 0; i < cpld->num_led_instances; i++) {
+ cpld->pled[i].cdev.name = cpld->profile[i].name;
+ cpld->pled[i].cdev.brightness = cpld->profile[i].brightness;
+ cpld->pled[i].cdev.max_brightness = 1;
+ cpld->pled[i].cdev.brightness_set = mlxcpld_led_brightness_set;
+ cpld->pled[i].cdev.blink_set = mlxcpld_led_blink_set;
+ cpld->pled[i].cdev.flags = LED_CORE_SUSPENDRESUME;
+ err = devm_led_classdev_register(dev, &cpld->pled[i].cdev);
+ if (err)
+ return err;
+
+ cpld->pled[i].param.offset = mlxcpld_led->profile[i].offset;
+ cpld->pled[i].param.mask = mlxcpld_led->profile[i].mask;
+ cpld->pled[i].param.base_color =
+ mlxcpld_led->profile[i].base_color;
+
+ if (mlxcpld_led->profile[i].brightness)
+ mlxcpld_led_brightness_set(&cpld->pled[i].cdev,
+ mlxcpld_led->profile[i].brightness);
+ }
+
+ return 0;
+}
+
+static int __init mlxcpld_led_probe(struct platform_device *pdev)
+{
+ enum mlxcpld_led_platform_types mlxcpld_led_plat =
+ mlxcpld_led_platform_check_sys_type();
+
+ mlxcpld_led = devm_kzalloc(&pdev->dev, sizeof(*mlxcpld_led),
+ GFP_KERNEL);
+ if (!mlxcpld_led)
+ return -ENOMEM;
+
+ mlxcpld_led->pdev = pdev;
+
+ switch (mlxcpld_led_plat) {
+ case MLXCPLD_LED_PLATFORM_MSN2100:
+ mlxcpld_led->profile = mlxcpld_led_msn2100_profile;
+ mlxcpld_led->num_led_instances =
+ ARRAY_SIZE(mlxcpld_led_msn2100_profile);
+ break;
+
+ default:
+ mlxcpld_led->profile = mlxcpld_led_default_profile;
+ mlxcpld_led->num_led_instances =
+ ARRAY_SIZE(mlxcpld_led_default_profile);
+ break;
+ }
+
+ spin_lock_init(&mlxcpld_led->lock);
+
+ return mlxcpld_led_config(&pdev->dev, mlxcpld_led);
+}
+
+static struct platform_driver mlxcpld_led_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+};
+
+static int __init mlxcpld_led_init(void)
+{
+ struct platform_device *pdev;
+ int err;
+
+ pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ pr_err("Device allocation failed\n");
+ return PTR_ERR(pdev);
+ }
+
+ err = platform_driver_probe(&mlxcpld_led_driver, mlxcpld_led_probe);
+ if (err) {
+ pr_err("Probe platform driver failed\n");
+ platform_device_unregister(pdev);
+ }
+
+ return err;
+}
+
+static void __exit mlxcpld_led_exit(void)
+{
+ platform_device_unregister(mlxcpld_led->pdev);
+ platform_driver_unregister(&mlxcpld_led_driver);
+}
+
+module_init(mlxcpld_led_init);
+module_exit(mlxcpld_led_exit);
+
+MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
+MODULE_DESCRIPTION("Mellanox board LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:leds_mlxcpld");
diff --git a/drivers/leds/leds-pm8058.c b/drivers/leds/leds-pm8058.c
new file mode 100644
index 0000000..a526743
--- /dev/null
+++ b/drivers/leds/leds-pm8058.c
@@ -0,0 +1,191 @@
+/* Copyright (c) 2010, 2011, 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+#define PM8058_LED_TYPE_COMMON 0x00
+#define PM8058_LED_TYPE_KEYPAD 0x01
+#define PM8058_LED_TYPE_FLASH 0x02
+
+#define PM8058_LED_TYPE_COMMON_MASK 0xf8
+#define PM8058_LED_TYPE_KEYPAD_MASK 0xf0
+#define PM8058_LED_TYPE_COMMON_SHIFT 3
+#define PM8058_LED_TYPE_KEYPAD_SHIFT 4
+
+struct pm8058_led {
+ struct regmap *map;
+ u32 reg;
+ u32 ledtype;
+ struct led_classdev cdev;
+};
+
+static void pm8058_led_set(struct led_classdev *cled,
+ enum led_brightness value)
+{
+ struct pm8058_led *led;
+ int ret = 0;
+ unsigned int mask = 0;
+ unsigned int val = 0;
+
+ led = container_of(cled, struct pm8058_led, cdev);
+ switch (led->ledtype) {
+ case PM8058_LED_TYPE_COMMON:
+ mask = PM8058_LED_TYPE_COMMON_MASK;
+ val = value << PM8058_LED_TYPE_COMMON_SHIFT;
+ break;
+ case PM8058_LED_TYPE_KEYPAD:
+ case PM8058_LED_TYPE_FLASH:
+ mask = PM8058_LED_TYPE_KEYPAD_MASK;
+ val = value << PM8058_LED_TYPE_KEYPAD_SHIFT;
+ break;
+ default:
+ break;
+ }
+
+ ret = regmap_update_bits(led->map, led->reg, mask, val);
+ if (ret)
+ pr_err("Failed to set LED brightness\n");
+}
+
+static enum led_brightness pm8058_led_get(struct led_classdev *cled)
+{
+ struct pm8058_led *led;
+ int ret;
+ unsigned int val;
+
+ led = container_of(cled, struct pm8058_led, cdev);
+
+ ret = regmap_read(led->map, led->reg, &val);
+ if (ret) {
+ pr_err("Failed to get LED brightness\n");
+ return LED_OFF;
+ }
+
+ switch (led->ledtype) {
+ case PM8058_LED_TYPE_COMMON:
+ val &= PM8058_LED_TYPE_COMMON_MASK;
+ val >>= PM8058_LED_TYPE_COMMON_SHIFT;
+ break;
+ case PM8058_LED_TYPE_KEYPAD:
+ case PM8058_LED_TYPE_FLASH:
+ val &= PM8058_LED_TYPE_KEYPAD_MASK;
+ val >>= PM8058_LED_TYPE_KEYPAD_SHIFT;
+ break;
+ default:
+ val = LED_OFF;
+ break;
+ }
+
+ return val;
+}
+
+static int pm8058_led_probe(struct platform_device *pdev)
+{
+ struct pm8058_led *led;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+ struct regmap *map;
+ const char *state;
+ enum led_brightness maxbright;
+
+ led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->ledtype = (u32)of_device_get_match_data(&pdev->dev);
+
+ map = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!map) {
+ dev_err(&pdev->dev, "Parent regmap unavailable.\n");
+ return -ENXIO;
+ }
+ led->map = map;
+
+ ret = of_property_read_u32(np, "reg", &led->reg);
+ if (ret) {
+ dev_err(&pdev->dev, "no register offset specified\n");
+ return -EINVAL;
+ }
+
+ /* Use label else node name */
+ led->cdev.name = of_get_property(np, "label", NULL) ? : np->name;
+ led->cdev.default_trigger =
+ of_get_property(np, "linux,default-trigger", NULL);
+ led->cdev.brightness_set = pm8058_led_set;
+ led->cdev.brightness_get = pm8058_led_get;
+ if (led->ledtype == PM8058_LED_TYPE_COMMON)
+ maxbright = 31; /* 5 bits */
+ else
+ maxbright = 15; /* 4 bits */
+ led->cdev.max_brightness = maxbright;
+
+ state = of_get_property(np, "default-state", NULL);
+ if (state) {
+ if (!strcmp(state, "keep")) {
+ led->cdev.brightness = pm8058_led_get(&led->cdev);
+ } else if (!strcmp(state, "on")) {
+ led->cdev.brightness = maxbright;
+ pm8058_led_set(&led->cdev, maxbright);
+ } else {
+ led->cdev.brightness = LED_OFF;
+ pm8058_led_set(&led->cdev, LED_OFF);
+ }
+ }
+
+ if (led->ledtype == PM8058_LED_TYPE_KEYPAD ||
+ led->ledtype == PM8058_LED_TYPE_FLASH)
+ led->cdev.flags = LED_CORE_SUSPENDRESUME;
+
+ ret = devm_led_classdev_register(&pdev->dev, &led->cdev);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to register led \"%s\"\n",
+ led->cdev.name);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id pm8058_leds_id_table[] = {
+ {
+ .compatible = "qcom,pm8058-led",
+ .data = (void *)PM8058_LED_TYPE_COMMON
+ },
+ {
+ .compatible = "qcom,pm8058-keypad-led",
+ .data = (void *)PM8058_LED_TYPE_KEYPAD
+ },
+ {
+ .compatible = "qcom,pm8058-flash-led",
+ .data = (void *)PM8058_LED_TYPE_FLASH
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8058_leds_id_table);
+
+static struct platform_driver pm8058_led_driver = {
+ .probe = pm8058_led_probe,
+ .driver = {
+ .name = "pm8058-leds",
+ .of_match_table = pm8058_leds_id_table,
+ },
+};
+module_platform_driver(pm8058_led_driver);
+
+MODULE_DESCRIPTION("PM8058 LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pm8058-leds");
diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index 9ebd2cf..c784ddc 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -1171,27 +1171,10 @@ static struct miscdevice _nvm_misc = {
.nodename = "lightnvm/control",
.fops = &_ctl_fops,
};
+module_misc_device(_nvm_misc);
MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
-static int __init nvm_mod_init(void)
-{
- int ret;
-
- ret = misc_register(&_nvm_misc);
- if (ret)
- pr_err("nvm: misc_register failed for control device");
-
- return ret;
-}
-
-static void __exit nvm_mod_exit(void)
-{
- misc_deregister(&_nvm_misc);
-}
-
MODULE_AUTHOR("Matias Bjorling <m@bjorling.me>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.1");
-module_init(nvm_mod_init);
-module_exit(nvm_mod_exit);
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 043828d..08c87fa 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -59,6 +59,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/mailbox_controller.h>
@@ -68,11 +69,16 @@
#include "mailbox.h"
#define MAX_PCC_SUBSPACES 256
+#define MBOX_IRQ_NAME "pcc-mbox"
static struct mbox_chan *pcc_mbox_channels;
/* Array of cached virtual address for doorbell registers */
static void __iomem **pcc_doorbell_vaddr;
+/* Array of cached virtual address for doorbell ack registers */
+static void __iomem **pcc_doorbell_ack_vaddr;
+/* Array of doorbell interrupts */
+static int *pcc_doorbell_irq;
static struct mbox_controller pcc_mbox_ctrl = {};
/**
@@ -91,6 +97,132 @@ static struct mbox_chan *get_pcc_channel(int id)
return &pcc_mbox_channels[id];
}
+/*
+ * PCC can be used with perf critical drivers such as CPPC
+ * So it makes sense to locally cache the virtual address and
+ * use it to read/write to PCC registers such as doorbell register
+ *
+ * The below read_register and write_registers are used to read and
+ * write from perf critical registers such as PCC doorbell register
+ */
+static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width)
+{
+ int ret_val = 0;
+
+ switch (bit_width) {
+ case 8:
+ *val = readb(vaddr);
+ break;
+ case 16:
+ *val = readw(vaddr);
+ break;
+ case 32:
+ *val = readl(vaddr);
+ break;
+ case 64:
+ *val = readq(vaddr);
+ break;
+ default:
+ pr_debug("Error: Cannot read register of %u bit width",
+ bit_width);
+ ret_val = -EFAULT;
+ break;
+ }
+ return ret_val;
+}
+
+static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
+{
+ int ret_val = 0;
+
+ switch (bit_width) {
+ case 8:
+ writeb(val, vaddr);
+ break;
+ case 16:
+ writew(val, vaddr);
+ break;
+ case 32:
+ writel(val, vaddr);
+ break;
+ case 64:
+ writeq(val, vaddr);
+ break;
+ default:
+ pr_debug("Error: Cannot write register of %u bit width",
+ bit_width);
+ ret_val = -EFAULT;
+ break;
+ }
+ return ret_val;
+}
+
+/**
+ * pcc_map_interrupt - Map a PCC subspace GSI to a linux IRQ number
+ * @interrupt: GSI number.
+ * @flags: interrupt flags
+ *
+ * Returns: a valid linux IRQ number on success
+ * 0 or -EINVAL on failure
+ */
+static int pcc_map_interrupt(u32 interrupt, u32 flags)
+{
+ int trigger, polarity;
+
+ if (!interrupt)
+ return 0;
+
+ trigger = (flags & ACPI_PCCT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
+ : ACPI_LEVEL_SENSITIVE;
+
+ polarity = (flags & ACPI_PCCT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
+ : ACPI_ACTIVE_HIGH;
+
+ return acpi_register_gsi(NULL, interrupt, trigger, polarity);
+}
+
+/**
+ * pcc_mbox_irq - PCC mailbox interrupt handler
+ */
+static irqreturn_t pcc_mbox_irq(int irq, void *p)
+{
+ struct acpi_generic_address *doorbell_ack;
+ struct acpi_pcct_hw_reduced *pcct_ss;
+ struct mbox_chan *chan = p;
+ u64 doorbell_ack_preserve;
+ u64 doorbell_ack_write;
+ u64 doorbell_ack_val;
+ int ret;
+
+ pcct_ss = chan->con_priv;
+
+ mbox_chan_received_data(chan, NULL);
+
+ if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
+ struct acpi_pcct_hw_reduced_type2 *pcct2_ss = chan->con_priv;
+ u32 id = chan - pcc_mbox_channels;
+
+ doorbell_ack = &pcct2_ss->doorbell_ack_register;
+ doorbell_ack_preserve = pcct2_ss->ack_preserve_mask;
+ doorbell_ack_write = pcct2_ss->ack_write_mask;
+
+ ret = read_register(pcc_doorbell_ack_vaddr[id],
+ &doorbell_ack_val,
+ doorbell_ack->bit_width);
+ if (ret)
+ return IRQ_NONE;
+
+ ret = write_register(pcc_doorbell_ack_vaddr[id],
+ (doorbell_ack_val & doorbell_ack_preserve)
+ | doorbell_ack_write,
+ doorbell_ack->bit_width);
+ if (ret)
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
/**
* pcc_mbox_request_channel - PCC clients call this function to
* request a pointer to their PCC subspace, from which they
@@ -135,6 +267,18 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
chan->txdone_method |= TXDONE_BY_ACK;
+ if (pcc_doorbell_irq[subspace_id] > 0) {
+ int rc;
+
+ rc = devm_request_irq(dev, pcc_doorbell_irq[subspace_id],
+ pcc_mbox_irq, 0, MBOX_IRQ_NAME, chan);
+ if (unlikely(rc)) {
+ dev_err(dev, "failed to register PCC interrupt %d\n",
+ pcc_doorbell_irq[subspace_id]);
+ chan = ERR_PTR(rc);
+ }
+ }
+
spin_unlock_irqrestore(&chan->lock, flags);
return chan;
@@ -149,80 +293,30 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
*/
void pcc_mbox_free_channel(struct mbox_chan *chan)
{
+ u32 id = chan - pcc_mbox_channels;
unsigned long flags;
if (!chan || !chan->cl)
return;
+ if (id >= pcc_mbox_ctrl.num_chans) {
+ pr_debug("pcc_mbox_free_channel: Invalid mbox_chan passed\n");
+ return;
+ }
+
spin_lock_irqsave(&chan->lock, flags);
chan->cl = NULL;
chan->active_req = NULL;
if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK))
chan->txdone_method = TXDONE_BY_POLL;
+ if (pcc_doorbell_irq[id] > 0)
+ devm_free_irq(chan->mbox->dev, pcc_doorbell_irq[id], chan);
+
spin_unlock_irqrestore(&chan->lock, flags);
}
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
-/*
- * PCC can be used with perf critical drivers such as CPPC
- * So it makes sense to locally cache the virtual address and
- * use it to read/write to PCC registers such as doorbell register
- *
- * The below read_register and write_registers are used to read and
- * write from perf critical registers such as PCC doorbell register
- */
-static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width)
-{
- int ret_val = 0;
-
- switch (bit_width) {
- case 8:
- *val = readb(vaddr);
- break;
- case 16:
- *val = readw(vaddr);
- break;
- case 32:
- *val = readl(vaddr);
- break;
- case 64:
- *val = readq(vaddr);
- break;
- default:
- pr_debug("Error: Cannot read register of %u bit width",
- bit_width);
- ret_val = -EFAULT;
- break;
- }
- return ret_val;
-}
-
-static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
-{
- int ret_val = 0;
-
- switch (bit_width) {
- case 8:
- writeb(val, vaddr);
- break;
- case 16:
- writew(val, vaddr);
- break;
- case 32:
- writel(val, vaddr);
- break;
- case 64:
- writeq(val, vaddr);
- break;
- default:
- pr_debug("Error: Cannot write register of %u bit width",
- bit_width);
- ret_val = -EFAULT;
- break;
- }
- return ret_val;
-}
/**
* pcc_send_data - Called from Mailbox Controller code. Used
@@ -296,8 +390,10 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header,
if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) {
pcct_ss = (struct acpi_pcct_hw_reduced *) header;
- if (pcct_ss->header.type !=
- ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) {
+ if ((pcct_ss->header.type !=
+ ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE)
+ && (pcct_ss->header.type !=
+ ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2)) {
pr_err("Incorrect PCC Subspace type detected\n");
return -EINVAL;
}
@@ -307,6 +403,43 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header,
}
/**
+ * pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register
+ * There should be one entry per PCC client.
+ * @id: PCC subspace index.
+ * @pcct_ss: Pointer to the ACPI subtable header under the PCCT.
+ *
+ * Return: 0 for Success, else errno.
+ *
+ * This gets called for each entry in the PCC table.
+ */
+static int pcc_parse_subspace_irq(int id,
+ struct acpi_pcct_hw_reduced *pcct_ss)
+{
+ pcc_doorbell_irq[id] = pcc_map_interrupt(pcct_ss->doorbell_interrupt,
+ (u32)pcct_ss->flags);
+ if (pcc_doorbell_irq[id] <= 0) {
+ pr_err("PCC GSI %d not registered\n",
+ pcct_ss->doorbell_interrupt);
+ return -EINVAL;
+ }
+
+ if (pcct_ss->header.type
+ == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
+ struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss;
+
+ pcc_doorbell_ack_vaddr[id] = acpi_os_ioremap(
+ pcct2_ss->doorbell_ack_register.address,
+ pcct2_ss->doorbell_ack_register.bit_width / 8);
+ if (!pcc_doorbell_ack_vaddr[id]) {
+ pr_err("Failed to ioremap PCC ACK register\n");
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/**
* acpi_pcc_probe - Parse the ACPI tree for the PCCT.
*
* Return: 0 for Success, else errno.
@@ -316,7 +449,9 @@ static int __init acpi_pcc_probe(void)
acpi_size pcct_tbl_header_size;
struct acpi_table_header *pcct_tbl;
struct acpi_subtable_header *pcct_entry;
- int count, i;
+ struct acpi_table_pcct *acpi_pcct_tbl;
+ int count, i, rc;
+ int sum = 0;
acpi_status status = AE_OK;
/* Search for PCCT */
@@ -333,37 +468,66 @@ static int __init acpi_pcc_probe(void)
sizeof(struct acpi_table_pcct),
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE,
parse_pcc_subspace, MAX_PCC_SUBSPACES);
+ sum += (count > 0) ? count : 0;
+
+ count = acpi_table_parse_entries(ACPI_SIG_PCCT,
+ sizeof(struct acpi_table_pcct),
+ ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2,
+ parse_pcc_subspace, MAX_PCC_SUBSPACES);
+ sum += (count > 0) ? count : 0;
- if (count <= 0) {
+ if (sum == 0 || sum >= MAX_PCC_SUBSPACES) {
pr_err("Error parsing PCC subspaces from PCCT\n");
return -EINVAL;
}
pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) *
- count, GFP_KERNEL);
-
+ sum, GFP_KERNEL);
if (!pcc_mbox_channels) {
pr_err("Could not allocate space for PCC mbox channels\n");
return -ENOMEM;
}
- pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL);
+ pcc_doorbell_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_vaddr) {
- kfree(pcc_mbox_channels);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto err_free_mbox;
+ }
+
+ pcc_doorbell_ack_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL);
+ if (!pcc_doorbell_ack_vaddr) {
+ rc = -ENOMEM;
+ goto err_free_db_vaddr;
+ }
+
+ pcc_doorbell_irq = kcalloc(sum, sizeof(int), GFP_KERNEL);
+ if (!pcc_doorbell_irq) {
+ rc = -ENOMEM;
+ goto err_free_db_ack_vaddr;
}
/* Point to the first PCC subspace entry */
pcct_entry = (struct acpi_subtable_header *) (
(unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct));
- for (i = 0; i < count; i++) {
+ acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl;
+ if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL)
+ pcc_mbox_ctrl.txdone_irq = true;
+
+ for (i = 0; i < sum; i++) {
struct acpi_generic_address *db_reg;
struct acpi_pcct_hw_reduced *pcct_ss;
pcc_mbox_channels[i].con_priv = pcct_entry;
+ pcct_ss = (struct acpi_pcct_hw_reduced *) pcct_entry;
+
+ if (pcc_mbox_ctrl.txdone_irq) {
+ rc = pcc_parse_subspace_irq(i, pcct_ss);
+ if (rc < 0)
+ goto err;
+ }
+
/* If doorbell is in system memory cache the virt address */
- pcct_ss = (struct acpi_pcct_hw_reduced *)pcct_entry;
db_reg = &pcct_ss->doorbell_register;
if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address,
@@ -372,11 +536,21 @@ static int __init acpi_pcc_probe(void)
((unsigned long) pcct_entry + pcct_entry->length);
}
- pcc_mbox_ctrl.num_chans = count;
+ pcc_mbox_ctrl.num_chans = sum;
pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans);
return 0;
+
+err:
+ kfree(pcc_doorbell_irq);
+err_free_db_ack_vaddr:
+ kfree(pcc_doorbell_ack_vaddr);
+err_free_db_vaddr:
+ kfree(pcc_doorbell_vaddr);
+err_free_mbox:
+ kfree(pcc_mbox_channels);
+ return rc;
}
/**
diff --git a/drivers/mcb/Kconfig b/drivers/mcb/Kconfig
index e9a6976..76d9c51 100644
--- a/drivers/mcb/Kconfig
+++ b/drivers/mcb/Kconfig
@@ -28,4 +28,13 @@ config MCB_PCI
If build as a module, the module is called mcb-pci.ko
+config MCB_LPC
+ tristate "LPC (non PCI) based MCB carrier"
+ default n
+ help
+
+ This is a MCB carrier on a LPC or non PCI device.
+
+ If build as a module, the module is called mcb-lpc.ko
+
endif # MCB
diff --git a/drivers/mcb/Makefile b/drivers/mcb/Makefile
index 1ae1413..bcc7745 100644
--- a/drivers/mcb/Makefile
+++ b/drivers/mcb/Makefile
@@ -5,3 +5,4 @@ mcb-y += mcb-core.o
mcb-y += mcb-parse.o
obj-$(CONFIG_MCB_PCI) += mcb-pci.o
+obj-$(CONFIG_MCB_LPC) += mcb-lpc.o
diff --git a/drivers/mcb/mcb-core.c b/drivers/mcb/mcb-core.c
index 6f2c852..921a5d2 100644
--- a/drivers/mcb/mcb-core.c
+++ b/drivers/mcb/mcb-core.c
@@ -233,6 +233,7 @@ int mcb_device_register(struct mcb_bus *bus, struct mcb_device *dev)
dev->dev.bus = &mcb_bus_type;
dev->dev.parent = bus->dev.parent;
dev->dev.release = mcb_release_dev;
+ dev->dma_dev = bus->carrier;
device_id = dev->id;
dev_set_name(&dev->dev, "mcb%d-16z%03d-%d:%d:%d",
@@ -369,7 +370,6 @@ struct mcb_device *mcb_alloc_dev(struct mcb_bus *bus)
if (!dev)
return NULL;
- INIT_LIST_HEAD(&dev->bus_list);
dev->bus = bus;
return dev;
@@ -405,20 +405,6 @@ static int __mcb_bus_add_devices(struct device *dev, void *data)
return 0;
}
-static int __mcb_bus_add_child(struct device *dev, void *data)
-{
- struct mcb_device *mdev = to_mcb_device(dev);
- struct mcb_bus *child;
-
- BUG_ON(!mdev->is_added);
- child = mdev->subordinate;
-
- if (child)
- mcb_bus_add_devices(child);
-
- return 0;
-}
-
/**
* mcb_bus_add_devices() - Add devices in the bus' internal device list
* @bus: The @mcb_bus we add the devices
@@ -428,8 +414,6 @@ static int __mcb_bus_add_child(struct device *dev, void *data)
void mcb_bus_add_devices(const struct mcb_bus *bus)
{
bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_devices);
- bus_for_each_dev(&mcb_bus_type, NULL, NULL, __mcb_bus_add_child);
-
}
EXPORT_SYMBOL_GPL(mcb_bus_add_devices);
diff --git a/drivers/mcb/mcb-internal.h b/drivers/mcb/mcb-internal.h
index 5254e02..d6e6933 100644
--- a/drivers/mcb/mcb-internal.h
+++ b/drivers/mcb/mcb-internal.h
@@ -112,6 +112,15 @@ struct chameleon_bdd {
u32 size;
} __packed;
+struct chameleon_bar {
+ u32 addr;
+ u32 size;
+};
+
+#define BAR_CNT(x) ((x) & 0x07)
+#define CHAMELEON_BAR_MAX 6
+#define BAR_DESC_SIZE(x) ((x) * sizeof(struct chameleon_bar) + sizeof(__le32))
+
int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
void __iomem *base);
diff --git a/drivers/mcb/mcb-lpc.c b/drivers/mcb/mcb-lpc.c
new file mode 100644
index 0000000..d072c08
--- /dev/null
+++ b/drivers/mcb/mcb-lpc.c
@@ -0,0 +1,158 @@
+/*
+ * MEN Chameleon Bus.
+ *
+ * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
+ * Author: Andreas Werner <andreas.werner@men.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/mcb.h>
+#include <linux/io.h>
+#include "mcb-internal.h"
+
+struct priv {
+ struct mcb_bus *bus;
+ struct resource *mem;
+ void __iomem *base;
+};
+
+static int mcb_lpc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct priv *priv;
+ int ret = 0;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!priv->mem) {
+ dev_err(&pdev->dev, "No Memory resource\n");
+ return -ENODEV;
+ }
+
+ res = devm_request_mem_region(&pdev->dev, priv->mem->start,
+ resource_size(priv->mem),
+ KBUILD_MODNAME);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to request IO memory\n");
+ return -EBUSY;
+ }
+
+ priv->base = devm_ioremap(&pdev->dev, priv->mem->start,
+ resource_size(priv->mem));
+ if (!priv->base) {
+ dev_err(&pdev->dev, "Cannot ioremap\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->bus = mcb_alloc_bus(&pdev->dev);
+ if (IS_ERR(priv->bus))
+ return PTR_ERR(priv->bus);
+
+ ret = chameleon_parse_cells(priv->bus, priv->mem->start, priv->base);
+ if (ret < 0) {
+ mcb_release_bus(priv->bus);
+ return ret;
+ }
+
+ dev_dbg(&pdev->dev, "Found %d cells\n", ret);
+
+ mcb_bus_add_devices(priv->bus);
+
+ return 0;
+
+}
+
+static int mcb_lpc_remove(struct platform_device *pdev)
+{
+ struct priv *priv = platform_get_drvdata(pdev);
+
+ mcb_release_bus(priv->bus);
+
+ return 0;
+}
+
+static struct platform_device *mcb_lpc_pdev;
+
+static int mcb_lpc_create_platform_device(const struct dmi_system_id *id)
+{
+ struct resource *res = id->driver_data;
+ int ret;
+
+ mcb_lpc_pdev = platform_device_alloc("mcb-lpc", -1);
+ if (!mcb_lpc_pdev)
+ return -ENOMEM;
+
+ ret = platform_device_add_resources(mcb_lpc_pdev, res, 1);
+ if (ret)
+ goto out_put;
+
+ ret = platform_device_add(mcb_lpc_pdev);
+ if (ret)
+ goto out_put;
+
+ return 0;
+
+out_put:
+ platform_device_put(mcb_lpc_pdev);
+ return ret;
+}
+
+static struct resource sc24_fpga_resource = {
+ .start = 0xe000e000,
+ .end = 0xe000e000 + CHAM_HEADER_SIZE,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_driver mcb_lpc_driver = {
+ .driver = {
+ .name = "mcb-lpc",
+ },
+ .probe = mcb_lpc_probe,
+ .remove = mcb_lpc_remove,
+};
+
+static const struct dmi_system_id mcb_lpc_dmi_table[] = {
+ {
+ .ident = "SC24",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEN"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "14SC24"),
+ },
+ .driver_data = (void *)&sc24_fpga_resource,
+ .callback = mcb_lpc_create_platform_device,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table);
+
+static int __init mcb_lpc_init(void)
+{
+ if (!dmi_check_system(mcb_lpc_dmi_table))
+ return -ENODEV;
+
+ return platform_driver_register(&mcb_lpc_driver);
+}
+
+static void __exit mcb_lpc_exit(void)
+{
+ platform_device_unregister(mcb_lpc_pdev);
+ platform_driver_unregister(&mcb_lpc_driver);
+}
+
+module_init(mcb_lpc_init);
+module_exit(mcb_lpc_exit);
+
+MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MCB over LPC support");
diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c
index dbecbed..4ca2739 100644
--- a/drivers/mcb/mcb-parse.c
+++ b/drivers/mcb/mcb-parse.c
@@ -26,19 +26,20 @@ static inline uint32_t get_next_dtype(void __iomem *p)
}
static int chameleon_parse_bdd(struct mcb_bus *bus,
- phys_addr_t mapbase,
+ struct chameleon_bar *cb,
void __iomem *base)
{
return 0;
}
static int chameleon_parse_gdd(struct mcb_bus *bus,
- phys_addr_t mapbase,
- void __iomem *base)
+ struct chameleon_bar *cb,
+ void __iomem *base, int bar_count)
{
struct chameleon_gdd __iomem *gdd =
(struct chameleon_gdd __iomem *) base;
struct mcb_device *mdev;
+ u32 dev_mapbase;
u32 offset;
u32 size;
int ret;
@@ -61,13 +62,39 @@ static int chameleon_parse_gdd(struct mcb_bus *bus,
mdev->group = GDD_GRP(reg2);
mdev->inst = GDD_INS(reg2);
+ /*
+ * If the BAR is missing, dev_mapbase is zero, or if the
+ * device is IO mapped we just print a warning and go on with the
+ * next device, instead of completely stop the gdd parser
+ */
+ if (mdev->bar > bar_count - 1) {
+ pr_info("No BAR for 16z%03d\n", mdev->id);
+ ret = 0;
+ goto err;
+ }
+
+ dev_mapbase = cb[mdev->bar].addr;
+ if (!dev_mapbase) {
+ pr_info("BAR not assigned for 16z%03d\n", mdev->id);
+ ret = 0;
+ goto err;
+ }
+
+ if (dev_mapbase & 0x01) {
+ pr_info("IO mapped Device (16z%03d) not yet supported\n",
+ mdev->id);
+ ret = 0;
+ goto err;
+ }
+
pr_debug("Found a 16z%03d\n", mdev->id);
mdev->irq.start = GDD_IRQ(reg1);
mdev->irq.end = GDD_IRQ(reg1);
mdev->irq.flags = IORESOURCE_IRQ;
- mdev->mem.start = mapbase + offset;
+ mdev->mem.start = dev_mapbase + offset;
+
mdev->mem.end = mdev->mem.start + size - 1;
mdev->mem.flags = IORESOURCE_MEM;
@@ -85,13 +112,76 @@ err:
return ret;
}
+static void chameleon_parse_bar(void __iomem *base,
+ struct chameleon_bar *cb, int bar_count)
+{
+ char __iomem *p = base;
+ int i;
+
+ /* skip reg1 */
+ p += sizeof(__le32);
+
+ for (i = 0; i < bar_count; i++) {
+ cb[i].addr = readl(p);
+ cb[i].size = readl(p + 4);
+
+ p += sizeof(struct chameleon_bar);
+ }
+}
+
+static int chameleon_get_bar(char __iomem **base, phys_addr_t mapbase,
+ struct chameleon_bar **cb)
+{
+ struct chameleon_bar *c;
+ int bar_count;
+ __le32 reg;
+ u32 dtype;
+
+ /*
+ * For those devices which are not connected
+ * to the PCI Bus (e.g. LPC) there is a bar
+ * descriptor located directly after the
+ * chameleon header. This header is comparable
+ * to a PCI header.
+ */
+ dtype = get_next_dtype(*base);
+ if (dtype == CHAMELEON_DTYPE_BAR) {
+ reg = readl(*base);
+
+ bar_count = BAR_CNT(reg);
+ if (bar_count <= 0 && bar_count > CHAMELEON_BAR_MAX)
+ return -ENODEV;
+
+ c = kcalloc(bar_count, sizeof(struct chameleon_bar),
+ GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ chameleon_parse_bar(*base, c, bar_count);
+ *base += BAR_DESC_SIZE(bar_count);
+ } else {
+ c = kzalloc(sizeof(struct chameleon_bar), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ bar_count = 1;
+ c->addr = mapbase;
+ }
+
+ *cb = c;
+
+ return bar_count;
+}
+
int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
void __iomem *base)
{
- char __iomem *p = base;
struct chameleon_fpga_header *header;
- uint32_t dtype;
+ struct chameleon_bar *cb;
+ char __iomem *p = base;
int num_cells = 0;
+ uint32_t dtype;
+ int bar_count;
int ret = 0;
u32 hsize;
@@ -108,8 +198,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
if (header->magic != CHAMELEONV2_MAGIC) {
pr_err("Unsupported chameleon version 0x%x\n",
header->magic);
- kfree(header);
- return -ENODEV;
+ ret = -ENODEV;
+ goto free_header;
}
p += hsize;
@@ -119,16 +209,20 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
snprintf(bus->name, CHAMELEON_FILENAME_LEN + 1, "%s",
header->filename);
+ bar_count = chameleon_get_bar(&p, mapbase, &cb);
+ if (bar_count < 0)
+ goto free_header;
+
for_each_chameleon_cell(dtype, p) {
switch (dtype) {
case CHAMELEON_DTYPE_GENERAL:
- ret = chameleon_parse_gdd(bus, mapbase, p);
+ ret = chameleon_parse_gdd(bus, cb, p, bar_count);
if (ret < 0)
- goto out;
+ goto free_bar;
p += sizeof(struct chameleon_gdd);
break;
case CHAMELEON_DTYPE_BRIDGE:
- chameleon_parse_bdd(bus, mapbase, p);
+ chameleon_parse_bdd(bus, cb, p);
p += sizeof(struct chameleon_bdd);
break;
case CHAMELEON_DTYPE_END:
@@ -136,8 +230,8 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
default:
pr_err("Invalid chameleon descriptor type 0x%x\n",
dtype);
- kfree(header);
- return -EINVAL;
+ ret = -EINVAL;
+ goto free_bar;
}
num_cells++;
}
@@ -145,11 +239,15 @@ int chameleon_parse_cells(struct mcb_bus *bus, phys_addr_t mapbase,
if (num_cells == 0)
num_cells = -EINVAL;
+ kfree(cb);
kfree(header);
return num_cells;
-out:
+free_bar:
+ kfree(cb);
+free_header:
kfree(header);
+
return ret;
}
EXPORT_SYMBOL_GPL(chameleon_parse_cells);
diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c
index b15a034..af4d2f2 100644
--- a/drivers/mcb/mcb-pci.c
+++ b/drivers/mcb/mcb-pci.c
@@ -46,6 +46,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev_err(&pdev->dev, "Failed to enable PCI device\n");
return -ENODEV;
}
+ pci_set_master(pdev);
priv->mapbase = pci_resource_start(pdev, 0);
if (!priv->mapbase) {
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index ee7fc37..5287e79 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6349,22 +6349,20 @@ static int alloc_scratch_buffer(struct r5conf *conf, struct raid5_percpu *percpu
return 0;
}
-static void raid5_free_percpu(struct r5conf *conf)
+static int raid456_cpu_dead(unsigned int cpu, struct hlist_node *node)
{
- unsigned long cpu;
+ struct r5conf *conf = hlist_entry_safe(node, struct r5conf, node);
+
+ free_scratch_buffer(conf, per_cpu_ptr(conf->percpu, cpu));
+ return 0;
+}
+static void raid5_free_percpu(struct r5conf *conf)
+{
if (!conf->percpu)
return;
-#ifdef CONFIG_HOTPLUG_CPU
- unregister_cpu_notifier(&conf->cpu_notify);
-#endif
-
- get_online_cpus();
- for_each_possible_cpu(cpu)
- free_scratch_buffer(conf, per_cpu_ptr(conf->percpu, cpu));
- put_online_cpus();
-
+ cpuhp_state_remove_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
free_percpu(conf->percpu);
}
@@ -6383,64 +6381,28 @@ static void free_conf(struct r5conf *conf)
kfree(conf);
}
-#ifdef CONFIG_HOTPLUG_CPU
-static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action,
- void *hcpu)
+static int raid456_cpu_up_prepare(unsigned int cpu, struct hlist_node *node)
{
- struct r5conf *conf = container_of(nfb, struct r5conf, cpu_notify);
- long cpu = (long)hcpu;
+ struct r5conf *conf = hlist_entry_safe(node, struct r5conf, node);
struct raid5_percpu *percpu = per_cpu_ptr(conf->percpu, cpu);
- switch (action) {
- case CPU_UP_PREPARE:
- case CPU_UP_PREPARE_FROZEN:
- if (alloc_scratch_buffer(conf, percpu)) {
- pr_err("%s: failed memory allocation for cpu%ld\n",
- __func__, cpu);
- return notifier_from_errno(-ENOMEM);
- }
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- case CPU_UP_CANCELED:
- case CPU_UP_CANCELED_FROZEN:
- free_scratch_buffer(conf, per_cpu_ptr(conf->percpu, cpu));
- break;
- default:
- break;
+ if (alloc_scratch_buffer(conf, percpu)) {
+ pr_err("%s: failed memory allocation for cpu%u\n",
+ __func__, cpu);
+ return -ENOMEM;
}
- return NOTIFY_OK;
+ return 0;
}
-#endif
static int raid5_alloc_percpu(struct r5conf *conf)
{
- unsigned long cpu;
int err = 0;
conf->percpu = alloc_percpu(struct raid5_percpu);
if (!conf->percpu)
return -ENOMEM;
-#ifdef CONFIG_HOTPLUG_CPU
- conf->cpu_notify.notifier_call = raid456_cpu_notify;
- conf->cpu_notify.priority = 0;
- err = register_cpu_notifier(&conf->cpu_notify);
- if (err)
- return err;
-#endif
-
- get_online_cpus();
- for_each_present_cpu(cpu) {
- err = alloc_scratch_buffer(conf, per_cpu_ptr(conf->percpu, cpu));
- if (err) {
- pr_err("%s: failed memory allocation for cpu%ld\n",
- __func__, cpu);
- break;
- }
- }
- put_online_cpus();
-
+ err = cpuhp_state_add_instance(CPUHP_MD_RAID5_PREPARE, &conf->node);
if (!err) {
conf->scribble_disks = max(conf->raid_disks,
conf->previous_raid_disks);
@@ -7985,10 +7947,21 @@ static struct md_personality raid4_personality =
static int __init raid5_init(void)
{
+ int ret;
+
raid5_wq = alloc_workqueue("raid5wq",
WQ_UNBOUND|WQ_MEM_RECLAIM|WQ_CPU_INTENSIVE|WQ_SYSFS, 0);
if (!raid5_wq)
return -ENOMEM;
+
+ ret = cpuhp_setup_state_multi(CPUHP_MD_RAID5_PREPARE,
+ "md/raid5:prepare",
+ raid456_cpu_up_prepare,
+ raid456_cpu_dead);
+ if (ret) {
+ destroy_workqueue(raid5_wq);
+ return ret;
+ }
register_md_personality(&raid6_personality);
register_md_personality(&raid5_personality);
register_md_personality(&raid4_personality);
@@ -8000,6 +7973,7 @@ static void raid5_exit(void)
unregister_md_personality(&raid6_personality);
unregister_md_personality(&raid5_personality);
unregister_md_personality(&raid4_personality);
+ cpuhp_remove_multi_state(CPUHP_MD_RAID5_PREPARE);
destroy_workqueue(raid5_wq);
}
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 517d4b6..57ec49f 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -512,9 +512,7 @@ struct r5conf {
} __percpu *percpu;
int scribble_disks;
int scribble_sectors;
-#ifdef CONFIG_HOTPLUG_CPU
- struct notifier_block cpu_notify;
-#endif
+ struct hlist_node node;
/*
* Free stripes pool
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index 6e22af3..e038e88 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -392,7 +392,6 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev)
dev_dbg(&pdev->dev, "alloc urb=%d\n", i);
dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
if (!dev->urb_list[i]) {
- dev_dbg(&pdev->dev, "failed\n");
for (j = 0; j < i; j++)
usb_free_urb(dev->urb_list[j]);
return -ENOMEM;
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 091d793..4b132c2 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -627,7 +627,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!radio->int_in_urb) {
- dev_info(&intf->dev, "could not allocate int_in_urb");
retval = -ENOMEM;
goto err_intbuffer;
}
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 65f80b8..86cc70fe25 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -2211,16 +2211,11 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf,
goto exit;
}
rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!rx_urb) {
- dev_err(dev, "%s: usb_alloc_urb failed for IR urb", __func__);
+ if (!rx_urb)
goto rx_urb_alloc_failed;
- }
tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!tx_urb) {
- dev_err(dev, "%s: usb_alloc_urb failed for display urb",
- __func__);
+ if (!tx_urb)
goto tx_urb_alloc_failed;
- }
mutex_init(&ictx->lock);
spin_lock_init(&ictx->kc_lock);
@@ -2305,10 +2300,8 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
int ret = -ENOMEM;
rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!rx_urb) {
- pr_err("usb_alloc_urb failed for IR urb\n");
+ if (!rx_urb)
goto rx_urb_alloc_failed;
- }
mutex_lock(&ictx->lock);
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 399f44d..ec8016d 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -970,10 +970,8 @@ static int redrat3_dev_probe(struct usb_interface *intf,
/* set up bulk-in endpoint */
rr3->read_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!rr3->read_urb) {
- dev_err(dev, "Read urb allocation failure\n");
+ if (!rr3->read_urb)
goto error;
- }
rr3->ep_in = ep_in;
rr3->bulk_in_buf = usb_alloc_coherent(udev,
diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c
index fe031b0..3c556ee 100644
--- a/drivers/media/usb/airspy/airspy.c
+++ b/drivers/media/usb/airspy/airspy.c
@@ -426,7 +426,6 @@ static int airspy_alloc_urbs(struct airspy *s)
dev_dbg(s->dev, "alloc urb=%d\n", i);
s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
if (!s->urb_list[i]) {
- dev_dbg(s->dev, "failed\n");
for (j = 0; j < i; j++)
usb_free_urb(s->urb_list[j]);
return -ENOMEM;
diff --git a/drivers/media/usb/as102/as102_usb_drv.c b/drivers/media/usb/as102/as102_usb_drv.c
index 0e8030c..68c3a80 100644
--- a/drivers/media/usb/as102/as102_usb_drv.c
+++ b/drivers/media/usb/as102/as102_usb_drv.c
@@ -270,8 +270,6 @@ static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (urb == NULL) {
- dev_dbg(&dev->bus_adap.usb_dev->dev,
- "%s: usb_alloc_urb failed\n", __func__);
as102_free_usb_stream_buffer(dev);
return -ENOMEM;
}
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
index 82b0269..13b8387 100644
--- a/drivers/media/usb/au0828/au0828-video.c
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -245,7 +245,6 @@ static int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(max_packets, GFP_KERNEL);
if (!urb) {
- au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i);
au0828_uninit_isoc(dev);
return -ENOMEM;
}
diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c
index c1aa1ab..13620cd 100644
--- a/drivers/media/usb/cpia2/cpia2_usb.c
+++ b/drivers/media/usb/cpia2/cpia2_usb.c
@@ -662,7 +662,6 @@ static int submit_urbs(struct camera_data *cam)
}
urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
if (!urb) {
- ERR("%s: usb_alloc_urb error!\n", __func__);
for (j = 0; j < i; j++)
usb_free_urb(cam->sbuf[j].urb);
return -ENOMEM;
diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
index a6a9508..4cd5fa9 100644
--- a/drivers/media/usb/cx231xx/cx231xx-audio.c
+++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
@@ -293,7 +293,6 @@ static int cx231xx_init_audio_isoc(struct cx231xx *dev)
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (!urb) {
- dev_err(dev->dev, "usb_alloc_urb failed!\n");
for (j = 0; j < i; j++) {
usb_free_urb(dev->adev.urb[j]);
kfree(dev->adev.transfer_buffer[j]);
@@ -355,7 +354,6 @@ static int cx231xx_init_audio_bulk(struct cx231xx *dev)
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (!urb) {
- dev_err(dev->dev, "usb_alloc_urb failed!\n");
for (j = 0; j < i; j++) {
usb_free_urb(dev->adev.urb[j]);
kfree(dev->adev.transfer_buffer[j]);
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index 630f4fc..8ec05cb 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -1035,8 +1035,6 @@ int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(max_packets, GFP_KERNEL);
if (!urb) {
- dev_err(dev->dev,
- "cannot alloc isoc_ctl.urb %i\n", i);
cx231xx_uninit_isoc(dev);
return -ENOMEM;
}
@@ -1172,8 +1170,6 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) {
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
- dev_err(dev->dev,
- "cannot alloc bulk_ctl.urb %i\n", i);
cx231xx_uninit_bulk(dev);
return -ENOMEM;
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
index 15bb573..76e9019 100644
--- a/drivers/media/usb/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c
@@ -442,8 +442,6 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
- dev_err(dev->dev,
- "cannot alloc bulk_ctl.urb %i\n", i);
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index bf890c3..2679797 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -783,10 +783,8 @@ int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf)
/* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
purb = usb_alloc_urb(0, GFP_KERNEL);
- if (purb == NULL) {
- err("rc usb alloc urb failed");
+ if (purb == NULL)
return -ENOMEM;
- }
purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL);
if (purb->transfer_buffer == NULL) {
diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c
index 49a5f95..78f3687 100644
--- a/drivers/media/usb/em28xx/em28xx-audio.c
+++ b/drivers/media/usb/em28xx/em28xx-audio.c
@@ -850,7 +850,6 @@ static int em28xx_audio_urb_init(struct em28xx *dev)
urb = usb_alloc_urb(npackets, GFP_ATOMIC);
if (!urb) {
- em28xx_errdev("usb_alloc_urb failed!\n");
em28xx_audio_free_urb(dev);
return -ENOMEM;
}
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index 3745607..eebd5d7 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -934,7 +934,6 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
for (i = 0; i < usb_bufs->num_bufs; i++) {
urb = usb_alloc_urb(usb_bufs->num_packets, GFP_KERNEL);
if (!urb) {
- em28xx_err("cannot alloc usb_ctl.urb %i\n", i);
em28xx_uninit_usb_xfer(dev, mode);
return -ENOMEM;
}
diff --git a/drivers/media/usb/gspca/benq.c b/drivers/media/usb/gspca/benq.c
index 790baed..5fa67b7 100644
--- a/drivers/media/usb/gspca/benq.c
+++ b/drivers/media/usb/gspca/benq.c
@@ -95,10 +95,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
#define SD_NPKT 32
for (n = 0; n < 4; n++) {
urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
- if (!urb) {
- pr_err("usb_alloc_urb failed\n");
+ if (!urb)
return -ENOMEM;
- }
gspca_dev->urb[n] = urb;
urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
SD_PKT_SZ * SD_NPKT,
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index b17bd7e..af2395a 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -795,10 +795,8 @@ static int create_urbs(struct gspca_dev *gspca_dev,
for (n = 0; n < nurbs; n++) {
urb = usb_alloc_urb(npkt, GFP_KERNEL);
- if (!urb) {
- pr_err("usb_alloc_urb failed\n");
+ if (!urb)
return -ENOMEM;
- }
gspca_dev->urb[n] = urb;
urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
bsize,
diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c
index 0712b1b..40aaaa9 100644
--- a/drivers/media/usb/gspca/konica.c
+++ b/drivers/media/usb/gspca/konica.c
@@ -208,10 +208,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
packet_size =
le16_to_cpu(alt->endpoint[i].desc.wMaxPacketSize);
urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
- if (!urb) {
- pr_err("usb_alloc_urb failed\n");
+ if (!urb)
return -ENOMEM;
- }
gspca_dev->urb[n] = urb;
urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
packet_size * SD_NPKT,
diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c
index b1e229a..c2c8d12 100644
--- a/drivers/media/usb/hackrf/hackrf.c
+++ b/drivers/media/usb/hackrf/hackrf.c
@@ -691,7 +691,6 @@ static int hackrf_alloc_urbs(struct hackrf_dev *dev, bool rcv)
dev_dbg(dev->dev, "alloc urb=%d\n", i);
dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
if (!dev->urb_list[i]) {
- dev_dbg(dev->dev, "failed\n");
for (j = 0; j < i; j++)
usb_free_urb(dev->urb_list[j]);
return -ENOMEM;
diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c
index 2a3a8b4..6d43d75 100644
--- a/drivers/media/usb/hdpvr/hdpvr-video.c
+++ b/drivers/media/usb/hdpvr/hdpvr-video.c
@@ -155,10 +155,8 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
buf->dev = dev;
urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
+ if (!urb)
goto exit_urb;
- }
buf->urb = urb;
mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL,
diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c
index e7f167d..367eb7e 100644
--- a/drivers/media/usb/msi2500/msi2500.c
+++ b/drivers/media/usb/msi2500/msi2500.c
@@ -509,7 +509,6 @@ static int msi2500_isoc_init(struct msi2500_dev *dev)
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL) {
- dev_err(dev->dev, "Failed to allocate urb %d\n", i);
msi2500_isoc_cleanup(dev);
return -ENOMEM;
}
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index b51b27a..c4454c9 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -410,7 +410,6 @@ retry:
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL) {
- PWC_ERROR("Failed to allocate urb %d\n", i);
pwc_isoc_cleanup(pdev);
return -ENOMEM;
}
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index 43ba71a..9458eb0 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -2113,11 +2113,8 @@ static int s2255_start_readpipe(struct s2255_dev *dev)
pipe_info->state = 1;
pipe_info->err_count = 0;
pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!pipe_info->stream_urb) {
- dev_err(&dev->udev->dev,
- "ReadStream: Unable to alloc URB\n");
+ if (!pipe_info->stream_urb)
return -ENOMEM;
- }
/* transfer buffer allocated in board_init */
usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
pipe,
@@ -2290,10 +2287,8 @@ static int s2255_probe(struct usb_interface *interface,
}
dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->fw_data->fw_urb) {
- dev_err(&interface->dev, "out of memory!\n");
+ if (!dev->fw_data->fw_urb)
goto errorFWURB;
- }
dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
if (!dev->fw_data->pfw_data) {
diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c
index 6ecb0b4..ce8ebbe 100644
--- a/drivers/media/usb/stk1160/stk1160-video.c
+++ b/drivers/media/usb/stk1160/stk1160-video.c
@@ -457,10 +457,8 @@ int stk1160_alloc_isoc(struct stk1160 *dev)
for (i = 0; i < num_bufs; i++) {
urb = usb_alloc_urb(max_packets, GFP_KERNEL);
- if (!urb) {
- stk1160_err("cannot alloc urb[%d]\n", i);
+ if (!urb)
goto free_i_bufs;
- }
dev->isoc_ctl.urb[i] = urb;
#ifndef CONFIG_DMA_NONCOHERENT
diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index c21c4c0..db200c9 100644
--- a/drivers/media/usb/stkwebcam/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
@@ -452,10 +452,8 @@ static int stk_prepare_iso(struct stk_camera *dev)
STK_ERROR("isobuf data already allocated\n");
if (dev->isobufs[i].urb == NULL) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
- if (urb == NULL) {
- STK_ERROR("Failed to allocate URB %d\n", i);
+ if (urb == NULL)
goto isobufs_out;
- }
dev->isobufs[i].urb = urb;
} else {
STK_ERROR("Killing URB\n");
diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c
index 095f5db..0426b21 100644
--- a/drivers/media/usb/tm6000/tm6000-dvb.c
+++ b/drivers/media/usb/tm6000/tm6000-dvb.c
@@ -129,10 +129,8 @@ static int tm6000_start_stream(struct tm6000_core *dev)
}
dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (dvb->bulk_urb == NULL) {
- printk(KERN_ERR "tm6000: couldn't allocate urb\n");
+ if (dvb->bulk_urb == NULL)
return -ENOMEM;
- }
pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK);
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
index fa5e8bd..dee7e7d 100644
--- a/drivers/media/usb/tm6000/tm6000-video.c
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -635,7 +635,6 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev)
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(max_packets, GFP_KERNEL);
if (!urb) {
- tm6000_err("cannot alloc isoc_ctl.urb %i\n", i);
tm6000_uninit_isoc(dev);
usb_free_urb(urb);
return -ENOMEM;
diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c
index 52ac439..c23bf73 100644
--- a/drivers/media/usb/usbvision/usbvision-core.c
+++ b/drivers/media/usb/usbvision/usbvision-core.c
@@ -2303,11 +2303,8 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision)
struct urb *urb;
urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
- if (urb == NULL) {
- dev_err(&usbvision->dev->dev,
- "%s: usb_alloc_urb() failed\n", __func__);
+ if (urb == NULL)
return -ENOMEM;
- }
usbvision->sbuf[buf_idx].urb = urb;
usbvision->sbuf[buf_idx].data =
usb_alloc_coherent(usbvision->dev,
diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index 7433ba5..cc128db 100644
--- a/drivers/media/usb/zr364xx/zr364xx.c
+++ b/drivers/media/usb/zr364xx/zr364xx.c
@@ -1045,10 +1045,8 @@ static int zr364xx_start_readpipe(struct zr364xx_camera *cam)
pipe_info->state = 1;
pipe_info->err_count = 0;
pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!pipe_info->stream_urb) {
- dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n");
+ if (!pipe_info->stream_urb)
return -ENOMEM;
- }
/* transfer buffer allocated in board_init */
usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev,
pipe,
diff --git a/drivers/memory/of_memory.c b/drivers/memory/of_memory.c
index 9daf94b..568f05e 100644
--- a/drivers/memory/of_memory.c
+++ b/drivers/memory/of_memory.c
@@ -16,6 +16,7 @@
#include <linux/gfp.h>
#include <memory/jedec_ddr.h>
#include <linux/export.h>
+#include "of_memory.h"
/**
* of_get_min_tck() - extract min timing values for ddr
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index bd3aa45..c8dee47 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -984,6 +984,10 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
int ret;
struct resource *res;
+ /* If we have ACPI based watchdog use that instead */
+ if (acpi_has_watchdog())
+ return -ENODEV;
+
/* Setup power management base register */
pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c
index 80b9dc3..ba610ad 100644
--- a/drivers/mfd/tps65218.c
+++ b/drivers/mfd/tps65218.c
@@ -219,6 +219,7 @@ static int tps65218_probe(struct i2c_client *client,
struct tps65218 *tps;
const struct of_device_id *match;
int ret;
+ unsigned int chipid;
match = of_match_device(of_tps65218_match_table, &client->dev);
if (!match) {
@@ -250,6 +251,14 @@ static int tps65218_probe(struct i2c_client *client,
if (ret < 0)
return ret;
+ ret = tps65218_reg_read(tps, TPS65218_REG_CHIPID, &chipid);
+ if (ret) {
+ dev_err(tps->dev, "Failed to read chipid: %d\n", ret);
+ return ret;
+ }
+
+ tps->rev = chipid & TPS65218_CHIPID_REV_MASK;
+
ret = of_platform_populate(client->dev.of_node, NULL, NULL,
&client->dev);
if (ret < 0)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index d002528..64971ba 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -429,34 +429,6 @@ config ARM_CHARLCD
line and the Linux version on the second line, but that's
still useful.
-config BMP085
- tristate
- depends on SYSFS
-
-config BMP085_I2C
- tristate "BMP085 digital pressure sensor on I2C"
- select BMP085
- select REGMAP_I2C
- depends on I2C && SYSFS
- help
- Say Y here if you want to support Bosch Sensortec's digital pressure
- sensor hooked to an I2C bus.
-
- To compile this driver as a module, choose M here: the
- module will be called bmp085-i2c.
-
-config BMP085_SPI
- tristate "BMP085 digital pressure sensor on SPI"
- select BMP085
- select REGMAP_SPI
- depends on SPI_MASTER && SYSFS
- help
- Say Y here if you want to support Bosch Sensortec's digital pressure
- sensor hooked to an SPI bus.
-
- To compile this driver as a module, choose M here: the
- module will be called bmp085-spi.
-
config PCH_PHUB
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB"
select GENERIC_NET_UTILS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fb32516..3198336 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -9,9 +9,6 @@ obj-$(CONFIG_AD525X_DPOT_SPI) += ad525x_dpot-spi.o
obj-$(CONFIG_INTEL_MID_PTI) += pti.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
-obj-$(CONFIG_BMP085) += bmp085.o
-obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o
-obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o
obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o
obj-$(CONFIG_ICS932S401) += ics932s401.o
obj-$(CONFIG_LKDTM) += lkdtm.o
diff --git a/drivers/misc/bmp085-i2c.c b/drivers/misc/bmp085-i2c.c
deleted file mode 100644
index f35c218..0000000
--- a/drivers/misc/bmp085-i2c.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 2012 Bosch Sensortec GmbH
- * Copyright (c) 2012 Unixphere AB
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/err.h>
-#include "bmp085.h"
-
-#define BMP085_I2C_ADDRESS 0x77
-
-static const unsigned short normal_i2c[] = { BMP085_I2C_ADDRESS,
- I2C_CLIENT_END };
-
-static int bmp085_i2c_detect(struct i2c_client *client,
- struct i2c_board_info *info)
-{
- if (client->addr != BMP085_I2C_ADDRESS)
- return -ENODEV;
-
- return bmp085_detect(&client->dev);
-}
-
-static int bmp085_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int err;
- struct regmap *regmap = devm_regmap_init_i2c(client,
- &bmp085_regmap_config);
-
- if (IS_ERR(regmap)) {
- err = PTR_ERR(regmap);
- dev_err(&client->dev, "Failed to init regmap: %d\n", err);
- return err;
- }
-
- return bmp085_probe(&client->dev, regmap, client->irq);
-}
-
-static int bmp085_i2c_remove(struct i2c_client *client)
-{
- return bmp085_remove(&client->dev);
-}
-
-static const struct i2c_device_id bmp085_id[] = {
- { BMP085_NAME, 0 },
- { "bmp180", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, bmp085_id);
-
-static struct i2c_driver bmp085_i2c_driver = {
- .driver = {
- .name = BMP085_NAME,
- },
- .id_table = bmp085_id,
- .probe = bmp085_i2c_probe,
- .remove = bmp085_i2c_remove,
-
- .detect = bmp085_i2c_detect,
- .address_list = normal_i2c
-};
-
-module_i2c_driver(bmp085_i2c_driver);
-
-MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
-MODULE_DESCRIPTION("BMP085 I2C bus driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/bmp085-spi.c b/drivers/misc/bmp085-spi.c
deleted file mode 100644
index 17ecbf9..0000000
--- a/drivers/misc/bmp085-spi.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2012 Bosch Sensortec GmbH
- * Copyright (c) 2012 Unixphere AB
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/spi/spi.h>
-#include <linux/err.h>
-#include "bmp085.h"
-
-static int bmp085_spi_probe(struct spi_device *client)
-{
- int err;
- struct regmap *regmap;
-
- client->bits_per_word = 8;
- err = spi_setup(client);
- if (err < 0) {
- dev_err(&client->dev, "spi_setup failed!\n");
- return err;
- }
-
- regmap = devm_regmap_init_spi(client, &bmp085_regmap_config);
- if (IS_ERR(regmap)) {
- err = PTR_ERR(regmap);
- dev_err(&client->dev, "Failed to init regmap: %d\n", err);
- return err;
- }
-
- return bmp085_probe(&client->dev, regmap, client->irq);
-}
-
-static int bmp085_spi_remove(struct spi_device *client)
-{
- return bmp085_remove(&client->dev);
-}
-
-static const struct of_device_id bmp085_of_match[] = {
- { .compatible = "bosch,bmp085", },
- { },
-};
-MODULE_DEVICE_TABLE(of, bmp085_of_match);
-
-static const struct spi_device_id bmp085_id[] = {
- { "bmp180", 0 },
- { "bmp181", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(spi, bmp085_id);
-
-static struct spi_driver bmp085_spi_driver = {
- .driver = {
- .name = BMP085_NAME,
- .of_match_table = bmp085_of_match
- },
- .id_table = bmp085_id,
- .probe = bmp085_spi_probe,
- .remove = bmp085_spi_remove
-};
-
-module_spi_driver(bmp085_spi_driver);
-
-MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
-MODULE_DESCRIPTION("BMP085 SPI bus driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/bmp085.c b/drivers/misc/bmp085.c
deleted file mode 100644
index 9b313f7..0000000
--- a/drivers/misc/bmp085.c
+++ /dev/null
@@ -1,506 +0,0 @@
-/* Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
- * Copyright (c) 2012 Bosch Sensortec GmbH
- * Copyright (c) 2012 Unixphere AB
- *
- * This driver supports the bmp085 and bmp18x digital barometric pressure
- * and temperature sensors from Bosch Sensortec. The datasheets
- * are available from their website:
- * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP085-DS000-05.pdf
- * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMP180-DS000-07.pdf
- *
- * A pressure measurement is issued by reading from pressure0_input.
- * The return value ranges from 30000 to 110000 pascal with a resulution
- * of 1 pascal (0.01 millibar) which enables measurements from 9000m above
- * to 500m below sea level.
- *
- * The temperature can be read from temp0_input. Values range from
- * -400 to 850 representing the ambient temperature in degree celsius
- * multiplied by 10.The resolution is 0.1 celsius.
- *
- * Because ambient pressure is temperature dependent, a temperature
- * measurement will be executed automatically even if the user is reading
- * from pressure0_input. This happens if the last temperature measurement
- * has been executed more then one second ago.
- *
- * To decrease RMS noise from pressure measurements, the bmp085 can
- * autonomously calculate the average of up to eight samples. This is
- * set up by writing to the oversampling sysfs file. Accepted values
- * are 0, 1, 2 and 3. 2^x when x is the value written to this file
- * specifies the number of samples used to calculate the ambient pressure.
- * RMS noise is specified with six pascal (without averaging) and decreases
- * down to 3 pascal when using an oversampling setting of 3.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include "bmp085.h"
-#include <linux/interrupt.h>
-#include <linux/completion.h>
-#include <linux/gpio.h>
-
-#define BMP085_CHIP_ID 0x55
-#define BMP085_CALIBRATION_DATA_START 0xAA
-#define BMP085_CALIBRATION_DATA_LENGTH 11 /* 16 bit values */
-#define BMP085_CHIP_ID_REG 0xD0
-#define BMP085_CTRL_REG 0xF4
-#define BMP085_TEMP_MEASUREMENT 0x2E
-#define BMP085_PRESSURE_MEASUREMENT 0x34
-#define BMP085_CONVERSION_REGISTER_MSB 0xF6
-#define BMP085_CONVERSION_REGISTER_LSB 0xF7
-#define BMP085_CONVERSION_REGISTER_XLSB 0xF8
-#define BMP085_TEMP_CONVERSION_TIME 5
-
-struct bmp085_calibration_data {
- s16 AC1, AC2, AC3;
- u16 AC4, AC5, AC6;
- s16 B1, B2;
- s16 MB, MC, MD;
-};
-
-struct bmp085_data {
- struct device *dev;
- struct regmap *regmap;
- struct mutex lock;
- struct bmp085_calibration_data calibration;
- u8 oversampling_setting;
- u32 raw_temperature;
- u32 raw_pressure;
- u32 temp_measurement_period;
- unsigned long last_temp_measurement;
- u8 chip_id;
- s32 b6; /* calculated temperature correction coefficient */
- int irq;
- struct completion done;
-};
-
-static irqreturn_t bmp085_eoc_isr(int irq, void *devid)
-{
- struct bmp085_data *data = devid;
-
- complete(&data->done);
-
- return IRQ_HANDLED;
-}
-
-static s32 bmp085_read_calibration_data(struct bmp085_data *data)
-{
- u16 tmp[BMP085_CALIBRATION_DATA_LENGTH];
- struct bmp085_calibration_data *cali = &(data->calibration);
- s32 status = regmap_bulk_read(data->regmap,
- BMP085_CALIBRATION_DATA_START, (u8 *)tmp,
- (BMP085_CALIBRATION_DATA_LENGTH << 1));
- if (status < 0)
- return status;
-
- cali->AC1 = be16_to_cpu(tmp[0]);
- cali->AC2 = be16_to_cpu(tmp[1]);
- cali->AC3 = be16_to_cpu(tmp[2]);
- cali->AC4 = be16_to_cpu(tmp[3]);
- cali->AC5 = be16_to_cpu(tmp[4]);
- cali->AC6 = be16_to_cpu(tmp[5]);
- cali->B1 = be16_to_cpu(tmp[6]);
- cali->B2 = be16_to_cpu(tmp[7]);
- cali->MB = be16_to_cpu(tmp[8]);
- cali->MC = be16_to_cpu(tmp[9]);
- cali->MD = be16_to_cpu(tmp[10]);
- return 0;
-}
-
-static s32 bmp085_update_raw_temperature(struct bmp085_data *data)
-{
- u16 tmp;
- s32 status;
-
- mutex_lock(&data->lock);
-
- init_completion(&data->done);
-
- status = regmap_write(data->regmap, BMP085_CTRL_REG,
- BMP085_TEMP_MEASUREMENT);
- if (status < 0) {
- dev_err(data->dev,
- "Error while requesting temperature measurement.\n");
- goto exit;
- }
- wait_for_completion_timeout(&data->done, 1 + msecs_to_jiffies(
- BMP085_TEMP_CONVERSION_TIME));
-
- status = regmap_bulk_read(data->regmap, BMP085_CONVERSION_REGISTER_MSB,
- &tmp, sizeof(tmp));
- if (status < 0) {
- dev_err(data->dev,
- "Error while reading temperature measurement result\n");
- goto exit;
- }
- data->raw_temperature = be16_to_cpu(tmp);
- data->last_temp_measurement = jiffies;
- status = 0; /* everything ok, return 0 */
-
-exit:
- mutex_unlock(&data->lock);
- return status;
-}
-
-static s32 bmp085_update_raw_pressure(struct bmp085_data *data)
-{
- u32 tmp = 0;
- s32 status;
-
- mutex_lock(&data->lock);
-
- init_completion(&data->done);
-
- status = regmap_write(data->regmap, BMP085_CTRL_REG,
- BMP085_PRESSURE_MEASUREMENT +
- (data->oversampling_setting << 6));
- if (status < 0) {
- dev_err(data->dev,
- "Error while requesting pressure measurement.\n");
- goto exit;
- }
-
- /* wait for the end of conversion */
- wait_for_completion_timeout(&data->done, 1 + msecs_to_jiffies(
- 2+(3 << data->oversampling_setting)));
- /* copy data into a u32 (4 bytes), but skip the first byte. */
- status = regmap_bulk_read(data->regmap, BMP085_CONVERSION_REGISTER_MSB,
- ((u8 *)&tmp)+1, 3);
- if (status < 0) {
- dev_err(data->dev,
- "Error while reading pressure measurement results\n");
- goto exit;
- }
- data->raw_pressure = be32_to_cpu((tmp));
- data->raw_pressure >>= (8-data->oversampling_setting);
- status = 0; /* everything ok, return 0 */
-
-exit:
- mutex_unlock(&data->lock);
- return status;
-}
-
-/*
- * This function starts the temperature measurement and returns the value
- * in tenth of a degree celsius.
- */
-static s32 bmp085_get_temperature(struct bmp085_data *data, int *temperature)
-{
- struct bmp085_calibration_data *cali = &data->calibration;
- long x1, x2;
- int status;
-
- status = bmp085_update_raw_temperature(data);
- if (status < 0)
- goto exit;
-
- x1 = ((data->raw_temperature - cali->AC6) * cali->AC5) >> 15;
- x2 = (cali->MC << 11) / (x1 + cali->MD);
- data->b6 = x1 + x2 - 4000;
- /* if NULL just update b6. Used for pressure only measurements */
- if (temperature != NULL)
- *temperature = (x1+x2+8) >> 4;
-
-exit:
- return status;
-}
-
-/*
- * This function starts the pressure measurement and returns the value
- * in millibar. Since the pressure depends on the ambient temperature,
- * a temperature measurement is executed according to the given temperature
- * measurement period (default is 1 sec boundary). This period could vary
- * and needs to be adjusted according to the sensor environment, i.e. if big
- * temperature variations then the temperature needs to be read out often.
- */
-static s32 bmp085_get_pressure(struct bmp085_data *data, int *pressure)
-{
- struct bmp085_calibration_data *cali = &data->calibration;
- s32 x1, x2, x3, b3;
- u32 b4, b7;
- s32 p;
- int status;
-
- /* alt least every second force an update of the ambient temperature */
- if ((data->last_temp_measurement == 0) ||
- time_is_before_jiffies(data->last_temp_measurement + 1*HZ)) {
- status = bmp085_get_temperature(data, NULL);
- if (status < 0)
- return status;
- }
-
- status = bmp085_update_raw_pressure(data);
- if (status < 0)
- return status;
-
- x1 = (data->b6 * data->b6) >> 12;
- x1 *= cali->B2;
- x1 >>= 11;
-
- x2 = cali->AC2 * data->b6;
- x2 >>= 11;
-
- x3 = x1 + x2;
-
- b3 = (((((s32)cali->AC1) * 4 + x3) << data->oversampling_setting) + 2);
- b3 >>= 2;
-
- x1 = (cali->AC3 * data->b6) >> 13;
- x2 = (cali->B1 * ((data->b6 * data->b6) >> 12)) >> 16;
- x3 = (x1 + x2 + 2) >> 2;
- b4 = (cali->AC4 * (u32)(x3 + 32768)) >> 15;
-
- b7 = ((u32)data->raw_pressure - b3) *
- (50000 >> data->oversampling_setting);
- p = ((b7 < 0x80000000) ? ((b7 << 1) / b4) : ((b7 / b4) * 2));
-
- x1 = p >> 8;
- x1 *= x1;
- x1 = (x1 * 3038) >> 16;
- x2 = (-7357 * p) >> 16;
- p += (x1 + x2 + 3791) >> 4;
-
- *pressure = p;
-
- return 0;
-}
-
-/*
- * This function sets the chip-internal oversampling. Valid values are 0..3.
- * The chip will use 2^oversampling samples for internal averaging.
- * This influences the measurement time and the accuracy; larger values
- * increase both. The datasheet gives an overview on how measurement time,
- * accuracy and noise correlate.
- */
-static void bmp085_set_oversampling(struct bmp085_data *data,
- unsigned char oversampling)
-{
- if (oversampling > 3)
- oversampling = 3;
- data->oversampling_setting = oversampling;
-}
-
-/*
- * Returns the currently selected oversampling. Range: 0..3
- */
-static unsigned char bmp085_get_oversampling(struct bmp085_data *data)
-{
- return data->oversampling_setting;
-}
-
-/* sysfs callbacks */
-static ssize_t set_oversampling(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct bmp085_data *data = dev_get_drvdata(dev);
- unsigned long oversampling;
- int err = kstrtoul(buf, 10, &oversampling);
-
- if (err == 0) {
- mutex_lock(&data->lock);
- bmp085_set_oversampling(data, oversampling);
- mutex_unlock(&data->lock);
- return count;
- }
-
- return err;
-}
-
-static ssize_t show_oversampling(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct bmp085_data *data = dev_get_drvdata(dev);
-
- return sprintf(buf, "%u\n", bmp085_get_oversampling(data));
-}
-static DEVICE_ATTR(oversampling, S_IWUSR | S_IRUGO,
- show_oversampling, set_oversampling);
-
-
-static ssize_t show_temperature(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int temperature;
- int status;
- struct bmp085_data *data = dev_get_drvdata(dev);
-
- status = bmp085_get_temperature(data, &temperature);
- if (status < 0)
- return status;
- else
- return sprintf(buf, "%d\n", temperature);
-}
-static DEVICE_ATTR(temp0_input, S_IRUGO, show_temperature, NULL);
-
-
-static ssize_t show_pressure(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int pressure;
- int status;
- struct bmp085_data *data = dev_get_drvdata(dev);
-
- status = bmp085_get_pressure(data, &pressure);
- if (status < 0)
- return status;
- else
- return sprintf(buf, "%d\n", pressure);
-}
-static DEVICE_ATTR(pressure0_input, S_IRUGO, show_pressure, NULL);
-
-
-static struct attribute *bmp085_attributes[] = {
- &dev_attr_temp0_input.attr,
- &dev_attr_pressure0_input.attr,
- &dev_attr_oversampling.attr,
- NULL
-};
-
-static const struct attribute_group bmp085_attr_group = {
- .attrs = bmp085_attributes,
-};
-
-int bmp085_detect(struct device *dev)
-{
- struct bmp085_data *data = dev_get_drvdata(dev);
- unsigned int id;
- int ret;
-
- ret = regmap_read(data->regmap, BMP085_CHIP_ID_REG, &id);
- if (ret < 0)
- return ret;
-
- if (id != data->chip_id)
- return -ENODEV;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bmp085_detect);
-
-static void bmp085_get_of_properties(struct bmp085_data *data)
-{
-#ifdef CONFIG_OF
- struct device_node *np = data->dev->of_node;
- u32 prop;
-
- if (!np)
- return;
-
- if (!of_property_read_u32(np, "chip-id", &prop))
- data->chip_id = prop & 0xff;
-
- if (!of_property_read_u32(np, "temp-measurement-period", &prop))
- data->temp_measurement_period = (prop/100)*HZ;
-
- if (!of_property_read_u32(np, "default-oversampling", &prop))
- data->oversampling_setting = prop & 0xff;
-#endif
-}
-
-static int bmp085_init_client(struct bmp085_data *data)
-{
- int status = bmp085_read_calibration_data(data);
-
- if (status < 0)
- return status;
-
- /* default settings */
- data->chip_id = BMP085_CHIP_ID;
- data->last_temp_measurement = 0;
- data->temp_measurement_period = 1*HZ;
- data->oversampling_setting = 3;
-
- bmp085_get_of_properties(data);
-
- mutex_init(&data->lock);
-
- return 0;
-}
-
-struct regmap_config bmp085_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8
-};
-EXPORT_SYMBOL_GPL(bmp085_regmap_config);
-
-int bmp085_probe(struct device *dev, struct regmap *regmap, int irq)
-{
- struct bmp085_data *data;
- int err = 0;
-
- data = kzalloc(sizeof(struct bmp085_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
-
- dev_set_drvdata(dev, data);
- data->dev = dev;
- data->regmap = regmap;
- data->irq = irq;
-
- if (data->irq > 0) {
- err = devm_request_irq(dev, data->irq, bmp085_eoc_isr,
- IRQF_TRIGGER_RISING, "bmp085",
- data);
- if (err < 0)
- goto exit_free;
- }
-
- /* Initialize the BMP085 chip */
- err = bmp085_init_client(data);
- if (err < 0)
- goto exit_free;
-
- err = bmp085_detect(dev);
- if (err < 0) {
- dev_err(dev, "%s: chip_id failed!\n", BMP085_NAME);
- goto exit_free;
- }
-
- /* Register sysfs hooks */
- err = sysfs_create_group(&dev->kobj, &bmp085_attr_group);
- if (err)
- goto exit_free;
-
- dev_info(dev, "Successfully initialized %s!\n", BMP085_NAME);
-
- return 0;
-
-exit_free:
- kfree(data);
-exit:
- return err;
-}
-EXPORT_SYMBOL_GPL(bmp085_probe);
-
-int bmp085_remove(struct device *dev)
-{
- struct bmp085_data *data = dev_get_drvdata(dev);
-
- sysfs_remove_group(&data->dev->kobj, &bmp085_attr_group);
- kfree(data);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(bmp085_remove);
-
-MODULE_AUTHOR("Christoph Mair <christoph.mair@gmail.com>");
-MODULE_DESCRIPTION("BMP085 driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/bmp085.h b/drivers/misc/bmp085.h
deleted file mode 100644
index 8b8e3b1..0000000
--- a/drivers/misc/bmp085.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2012 Bosch Sensortec GmbH
- * Copyright (c) 2012 Unixphere AB
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _BMP085_H
-#define _BMP085_H
-
-#include <linux/regmap.h>
-
-#define BMP085_NAME "bmp085"
-
-extern struct regmap_config bmp085_regmap_config;
-
-int bmp085_probe(struct device *dev, struct regmap *regmap, int irq);
-int bmp085_remove(struct device *dev);
-int bmp085_detect(struct device *dev);
-
-#endif
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 2c6c7c8..5afe4cd 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -121,9 +121,8 @@ static int at25_ee_read(void *priv, unsigned int offset,
* this chip is clocked very slowly
*/
status = spi_sync(at25->spi, &m);
- dev_dbg(&at25->spi->dev,
- "read %Zd bytes at %d --> %d\n",
- count, offset, (int) status);
+ dev_dbg(&at25->spi->dev, "read %zu bytes at %d --> %zd\n",
+ count, offset, status);
mutex_unlock(&at25->lock);
return status;
@@ -167,8 +166,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
*cp = AT25_WREN;
status = spi_write(at25->spi, cp, 1);
if (status < 0) {
- dev_dbg(&at25->spi->dev, "WREN --> %d\n",
- (int) status);
+ dev_dbg(&at25->spi->dev, "WREN --> %d\n", status);
break;
}
@@ -196,9 +194,8 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
memcpy(cp, buf, segment);
status = spi_write(at25->spi, bounce,
segment + at25->addrlen + 1);
- dev_dbg(&at25->spi->dev,
- "write %u bytes at %u --> %d\n",
- segment, offset, (int) status);
+ dev_dbg(&at25->spi->dev, "write %u bytes at %u --> %d\n",
+ segment, offset, status);
if (status < 0)
break;
@@ -225,8 +222,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
if ((sr < 0) || (sr & AT25_SR_nRDY)) {
dev_err(&at25->spi->dev,
- "write %d bytes offset %d, "
- "timeout after %u msecs\n",
+ "write %u bytes offset %u, timeout after %u msecs\n",
segment, offset,
jiffies_to_msecs(jiffies -
(timeout - EE_TIMEOUT)));
@@ -368,9 +364,7 @@ static int at25_probe(struct spi_device *spi)
return PTR_ERR(at25->nvmem);
dev_info(&spi->dev, "%d %s %s eeprom%s, pagesize %u\n",
- (chip.byte_len < 1024)
- ? chip.byte_len
- : (chip.byte_len / 1024),
+ (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024),
(chip.byte_len < 1024) ? "Byte" : "KByte",
at25->chip.name,
(chip.flags & EE_READONLY) ? " (readonly)" : "",
diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c
index a70b853..6c1f49a 100644
--- a/drivers/misc/genwqe/card_base.c
+++ b/drivers/misc/genwqe/card_base.c
@@ -1351,6 +1351,19 @@ static struct pci_driver genwqe_driver = {
};
/**
+ * genwqe_devnode() - Set default access mode for genwqe devices.
+ *
+ * Default mode should be rw for everybody. Do not change default
+ * device name.
+ */
+static char *genwqe_devnode(struct device *dev, umode_t *mode)
+{
+ if (mode)
+ *mode = 0666;
+ return NULL;
+}
+
+/**
* genwqe_init_module() - Driver registration and initialization
*/
static int __init genwqe_init_module(void)
@@ -1363,6 +1376,8 @@ static int __init genwqe_init_module(void)
return -ENOMEM;
}
+ class_genwqe->devnode = genwqe_devnode;
+
debugfs_genwqe = debugfs_create_dir(GENWQE_DEVNAME, NULL);
if (!debugfs_genwqe) {
rc = -ENOMEM;
diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c
index 353ee0c..ddfeefe 100644
--- a/drivers/misc/genwqe/card_ddcb.c
+++ b/drivers/misc/genwqe/card_ddcb.c
@@ -1048,8 +1048,6 @@ static int setup_ddcb_queue(struct genwqe_dev *cd, struct ddcb_queue *queue)
"[%s] **err: could not allocate DDCB **\n", __func__);
return -ENOMEM;
}
- memset(queue->ddcb_vaddr, 0, queue_size);
-
queue->ddcb_req = kzalloc(sizeof(struct ddcb_requ *) *
queue->ddcb_max, GFP_KERNEL);
if (!queue->ddcb_req) {
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index 222367c..8a679ec 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -220,8 +220,8 @@ void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size,
if (get_order(size) > MAX_ORDER)
return NULL;
- return dma_alloc_coherent(&cd->pci_dev->dev, size, dma_handle,
- GFP_KERNEL);
+ return dma_zalloc_coherent(&cd->pci_dev->dev, size, dma_handle,
+ GFP_KERNEL);
}
void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size,
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c
index d6a901c..fea8ff4 100644
--- a/drivers/misc/hpilo.c
+++ b/drivers/misc/hpilo.c
@@ -688,7 +688,8 @@ static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
{
- int error = -ENOMEM;
+ int bar;
+ unsigned long off;
/* map the memory mapped i/o registers */
hw->mmio_vaddr = pci_iomap(pdev, 1, 0);
@@ -698,7 +699,15 @@ static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
}
/* map the adapter shared memory region */
- hw->ram_vaddr = pci_iomap(pdev, 2, max_ccb * ILOHW_CCB_SZ);
+ if (pdev->subsystem_device == 0x00E4) {
+ bar = 5;
+ /* Last 8k is reserved for CCBs */
+ off = pci_resource_len(pdev, bar) - 0x2000;
+ } else {
+ bar = 2;
+ off = 0;
+ }
+ hw->ram_vaddr = pci_iomap_range(pdev, bar, off, max_ccb * ILOHW_CCB_SZ);
if (hw->ram_vaddr == NULL) {
dev_err(&pdev->dev, "Error mapping shared mem\n");
goto mmio_free;
@@ -717,7 +726,7 @@ ram_free:
mmio_free:
pci_iounmap(pdev, hw->mmio_vaddr);
out:
- return error;
+ return -ENOMEM;
}
static void ilo_remove(struct pci_dev *pdev)
@@ -899,7 +908,7 @@ static void __exit ilo_exit(void)
class_destroy(ilo_class);
}
-MODULE_VERSION("1.4.1");
+MODULE_VERSION("1.5.0");
MODULE_ALIAS(ILO_NAME);
MODULE_DESCRIPTION(ILO_NAME);
MODULE_AUTHOR("David Altobelli <david.altobelli@hpe.com>");
diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c
index a039a5d..7ae89b4 100644
--- a/drivers/misc/mei/amthif.c
+++ b/drivers/misc/mei/amthif.c
@@ -47,7 +47,6 @@ const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
void mei_amthif_reset_params(struct mei_device *dev)
{
/* reset iamthif parameters. */
- dev->iamthif_current_cb = NULL;
dev->iamthif_canceled = false;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
dev->iamthif_stall_timer = 0;
@@ -67,8 +66,12 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
struct mei_cl *cl = &dev->iamthif_cl;
int ret;
- if (mei_cl_is_connected(cl))
- return 0;
+ mutex_lock(&dev->device_lock);
+
+ if (mei_cl_is_connected(cl)) {
+ ret = 0;
+ goto out;
+ }
dev->iamthif_state = MEI_IAMTHIF_IDLE;
@@ -77,179 +80,37 @@ int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
ret = mei_cl_link(cl);
if (ret < 0) {
dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
- return ret;
+ goto out;
}
ret = mei_cl_connect(cl, me_cl, NULL);
- return ret;
-}
-
-/**
- * mei_amthif_read - read data from AMTHIF client
- *
- * @dev: the device structure
- * @file: pointer to file object
- * @ubuf: pointer to user data in user space
- * @length: data length to read
- * @offset: data read offset
- *
- * Locking: called under "dev->device_lock" lock
- *
- * Return:
- * returned data length on success,
- * zero if no data to read,
- * negative on failure.
- */
-int mei_amthif_read(struct mei_device *dev, struct file *file,
- char __user *ubuf, size_t length, loff_t *offset)
-{
- struct mei_cl *cl = file->private_data;
- struct mei_cl_cb *cb;
- int rets;
- int wait_ret;
-
- dev_dbg(dev->dev, "checking amthif data\n");
- cb = mei_cl_read_cb(cl, file);
-
- /* Check for if we can block or not*/
- if (cb == NULL && file->f_flags & O_NONBLOCK)
- return -EAGAIN;
-
-
- dev_dbg(dev->dev, "waiting for amthif data\n");
- while (cb == NULL) {
- /* unlock the Mutex */
- mutex_unlock(&dev->device_lock);
-
- wait_ret = wait_event_interruptible(cl->rx_wait,
- !list_empty(&cl->rd_completed) ||
- !mei_cl_is_connected(cl));
-
- /* Locking again the Mutex */
- mutex_lock(&dev->device_lock);
-
- if (wait_ret)
- return -ERESTARTSYS;
-
- if (!mei_cl_is_connected(cl)) {
- rets = -EBUSY;
- goto out;
- }
-
- cb = mei_cl_read_cb(cl, file);
- }
-
- if (cb->status) {
- rets = cb->status;
- dev_dbg(dev->dev, "read operation failed %d\n", rets);
- goto free;
- }
-
- dev_dbg(dev->dev, "Got amthif data\n");
- /* if the whole message will fit remove it from the list */
- if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
- list_del_init(&cb->list);
- else if (cb->buf_idx <= *offset) {
- /* end of the message has been reached */
- list_del_init(&cb->list);
- rets = 0;
- goto free;
- }
- /* else means that not full buffer will be read and do not
- * remove message from deletion list
- */
-
- dev_dbg(dev->dev, "amthif cb->buf.size - %zu cb->buf_idx - %zu\n",
- cb->buf.size, cb->buf_idx);
-
- /* length is being truncated to PAGE_SIZE, however,
- * the buf_idx may point beyond */
- length = min_t(size_t, length, (cb->buf_idx - *offset));
-
- if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
- dev_dbg(dev->dev, "failed to copy data to userland\n");
- rets = -EFAULT;
- } else {
- rets = length;
- if ((*offset + length) < cb->buf_idx) {
- *offset += length;
- goto out;
- }
- }
-free:
- dev_dbg(dev->dev, "free amthif cb memory.\n");
- *offset = 0;
- mei_io_cb_free(cb);
out:
- return rets;
+ mutex_unlock(&dev->device_lock);
+ return ret;
}
/**
* mei_amthif_read_start - queue message for sending read credential
*
* @cl: host client
- * @file: file pointer of message recipient
+ * @fp: file pointer of message recipient
*
* Return: 0 on success, <0 on failure.
*/
-static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file)
+static int mei_amthif_read_start(struct mei_cl *cl, const struct file *fp)
{
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
- int rets;
-
- cb = mei_io_cb_init(cl, MEI_FOP_READ, file);
- if (!cb) {
- rets = -ENOMEM;
- goto err;
- }
- rets = mei_io_cb_alloc_buf(cb, mei_cl_mtu(cl));
- if (rets)
- goto err;
+ cb = mei_cl_enqueue_ctrl_wr_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, fp);
+ if (!cb)
+ return -ENOMEM;
- list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
+ cl->rx_flow_ctrl_creds++;
dev->iamthif_state = MEI_IAMTHIF_READING;
- dev->iamthif_fp = cb->fp;
- dev->iamthif_current_cb = cb;
-
- return 0;
-err:
- mei_io_cb_free(cb);
- return rets;
-}
-
-/**
- * mei_amthif_send_cmd - send amthif command to the ME
- *
- * @cl: the host client
- * @cb: mei call back struct
- *
- * Return: 0 on success, <0 on failure.
- */
-static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
-{
- struct mei_device *dev;
- int ret;
-
- if (!cl->dev || !cb)
- return -ENODEV;
-
- dev = cl->dev;
-
- dev->iamthif_state = MEI_IAMTHIF_WRITING;
- dev->iamthif_current_cb = cb;
- dev->iamthif_fp = cb->fp;
- dev->iamthif_canceled = false;
-
- ret = mei_cl_write(cl, cb, false);
- if (ret < 0)
- return ret;
-
- if (cb->completed)
- cb->status = mei_amthif_read_start(cl, cb->fp);
+ cl->fp = cb->fp;
return 0;
}
@@ -265,20 +126,32 @@ int mei_amthif_run_next_cmd(struct mei_device *dev)
{
struct mei_cl *cl = &dev->iamthif_cl;
struct mei_cl_cb *cb;
+ int ret;
dev->iamthif_canceled = false;
- dev->iamthif_state = MEI_IAMTHIF_IDLE;
- dev->iamthif_fp = NULL;
dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
typeof(*cb), list);
- if (!cb)
+ if (!cb) {
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ cl->fp = NULL;
return 0;
+ }
list_del_init(&cb->list);
- return mei_amthif_send_cmd(cl, cb);
+ dev->iamthif_state = MEI_IAMTHIF_WRITING;
+ cl->fp = cb->fp;
+
+ ret = mei_cl_write(cl, cb, false);
+ if (ret < 0)
+ return ret;
+
+ if (cb->completed)
+ cb->status = mei_amthif_read_start(cl, cb->fp);
+
+ return 0;
}
/**
@@ -299,8 +172,7 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
/*
* The previous request is still in processing, queue this one.
*/
- if (dev->iamthif_state > MEI_IAMTHIF_IDLE &&
- dev->iamthif_state < MEI_IAMTHIF_READ_COMPLETE)
+ if (dev->iamthif_state != MEI_IAMTHIF_IDLE)
return 0;
return mei_amthif_run_next_cmd(dev);
@@ -309,7 +181,6 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
/**
* mei_amthif_poll - the amthif poll function
*
- * @dev: the device structure
* @file: pointer to file structure
* @wait: pointer to poll_table structure
*
@@ -317,26 +188,19 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
*
* Locking: called under "dev->device_lock" lock
*/
-
-unsigned int mei_amthif_poll(struct mei_device *dev,
- struct file *file, poll_table *wait)
+unsigned int mei_amthif_poll(struct file *file, poll_table *wait)
{
+ struct mei_cl *cl = file->private_data;
+ struct mei_cl_cb *cb = mei_cl_read_cb(cl, file);
unsigned int mask = 0;
- poll_wait(file, &dev->iamthif_cl.rx_wait, wait);
-
- if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
- dev->iamthif_fp == file) {
-
+ poll_wait(file, &cl->rx_wait, wait);
+ if (cb)
mask |= POLLIN | POLLRDNORM;
- mei_amthif_run_next_cmd(dev);
- }
return mask;
}
-
-
/**
* mei_amthif_irq_write - write iamthif command in irq thread context.
*
@@ -393,7 +257,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl,
return 0;
dev_dbg(dev->dev, "completed amthif read.\n ");
- dev->iamthif_current_cb = NULL;
dev->iamthif_stall_timer = 0;
return 0;
@@ -409,115 +272,63 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
{
struct mei_device *dev = cl->dev;
- if (cb->fop_type == MEI_FOP_WRITE) {
+ dev_dbg(dev->dev, "completing amthif call back.\n");
+ switch (cb->fop_type) {
+ case MEI_FOP_WRITE:
if (!cb->status) {
dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
+ mei_schedule_stall_timer(dev);
mei_io_cb_free(cb);
return;
}
- /*
- * in case of error enqueue the write cb to complete read list
- * so it can be propagated to the reader
- */
- list_add_tail(&cb->list, &cl->rd_completed);
- wake_up_interruptible(&cl->rx_wait);
- return;
- }
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ cl->fp = NULL;
+ if (!dev->iamthif_canceled) {
+ /*
+ * in case of error enqueue the write cb to complete
+ * read list so it can be propagated to the reader
+ */
+ list_add_tail(&cb->list, &cl->rd_completed);
+ wake_up_interruptible(&cl->rx_wait);
+ } else {
+ mei_io_cb_free(cb);
+ }
+ break;
+ case MEI_FOP_READ:
+ if (!dev->iamthif_canceled) {
+ list_add_tail(&cb->list, &cl->rd_completed);
+ dev_dbg(dev->dev, "amthif read completed\n");
+ wake_up_interruptible(&cl->rx_wait);
+ } else {
+ mei_io_cb_free(cb);
+ }
- if (!dev->iamthif_canceled) {
- dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
dev->iamthif_stall_timer = 0;
- list_add_tail(&cb->list, &cl->rd_completed);
- dev_dbg(dev->dev, "amthif read completed\n");
- } else {
mei_amthif_run_next_cmd(dev);
+ break;
+ default:
+ WARN_ON(1);
}
-
- dev_dbg(dev->dev, "completing amthif call back.\n");
- wake_up_interruptible(&cl->rx_wait);
}
/**
* mei_clear_list - removes all callbacks associated with file
* from mei_cb_list
*
- * @dev: device structure.
* @file: file structure
* @mei_cb_list: callbacks list
*
* mei_clear_list is called to clear resources associated with file
* when application calls close function or Ctrl-C was pressed
- *
- * Return: true if callback removed from the list, false otherwise
*/
-static bool mei_clear_list(struct mei_device *dev,
- const struct file *file, struct list_head *mei_cb_list)
+static void mei_clear_list(const struct file *file,
+ struct list_head *mei_cb_list)
{
- struct mei_cl *cl = &dev->iamthif_cl;
struct mei_cl_cb *cb, *next;
- bool removed = false;
-
- /* list all list member */
- list_for_each_entry_safe(cb, next, mei_cb_list, list) {
- /* check if list member associated with a file */
- if (file == cb->fp) {
- /* check if cb equal to current iamthif cb */
- if (dev->iamthif_current_cb == cb) {
- dev->iamthif_current_cb = NULL;
- /* send flow control to iamthif client */
- mei_hbm_cl_flow_control_req(dev, cl);
- }
- /* free all allocated buffers */
- mei_io_cb_free(cb);
- removed = true;
- }
- }
- return removed;
-}
-/**
- * mei_clear_lists - removes all callbacks associated with file
- *
- * @dev: device structure
- * @file: file structure
- *
- * mei_clear_lists is called to clear resources associated with file
- * when application calls close function or Ctrl-C was pressed
- *
- * Return: true if callback removed from the list, false otherwise
- */
-static bool mei_clear_lists(struct mei_device *dev, const struct file *file)
-{
- bool removed = false;
- struct mei_cl *cl = &dev->iamthif_cl;
-
- /* remove callbacks associated with a file */
- mei_clear_list(dev, file, &dev->amthif_cmd_list.list);
- if (mei_clear_list(dev, file, &cl->rd_completed))
- removed = true;
-
- mei_clear_list(dev, file, &dev->ctrl_rd_list.list);
-
- if (mei_clear_list(dev, file, &dev->ctrl_wr_list.list))
- removed = true;
-
- if (mei_clear_list(dev, file, &dev->write_waiting_list.list))
- removed = true;
-
- if (mei_clear_list(dev, file, &dev->write_list.list))
- removed = true;
-
- /* check if iamthif_current_cb not NULL */
- if (dev->iamthif_current_cb && !removed) {
- /* check file and iamthif current cb association */
- if (dev->iamthif_current_cb->fp == file) {
- /* remove cb */
- mei_io_cb_free(dev->iamthif_current_cb);
- dev->iamthif_current_cb = NULL;
- removed = true;
- }
- }
- return removed;
+ list_for_each_entry_safe(cb, next, mei_cb_list, list)
+ if (file == cb->fp)
+ mei_io_cb_free(cb);
}
/**
@@ -530,23 +341,21 @@ static bool mei_clear_lists(struct mei_device *dev, const struct file *file)
*/
int mei_amthif_release(struct mei_device *dev, struct file *file)
{
+ struct mei_cl *cl = file->private_data;
+
if (dev->iamthif_open_count > 0)
dev->iamthif_open_count--;
- if (dev->iamthif_fp == file &&
- dev->iamthif_state != MEI_IAMTHIF_IDLE) {
+ if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) {
dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
dev->iamthif_state);
dev->iamthif_canceled = true;
- if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) {
- dev_dbg(dev->dev, "run next amthif iamthif cb\n");
- mei_amthif_run_next_cmd(dev);
- }
}
- if (mei_clear_lists(dev, file))
- dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ mei_clear_list(file, &dev->amthif_cmd_list.list);
+ mei_clear_list(file, &cl->rd_completed);
+ mei_clear_list(file, &dev->ctrl_rd_list.list);
return 0;
}
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 1f33fea..8cac7ef 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -126,7 +126,8 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
goto out;
/* wait on event only if there is no other waiter */
- if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
+ /* synchronized under device mutex */
+ if (!waitqueue_active(&cl->rx_wait)) {
mutex_unlock(&bus->device_lock);
@@ -142,7 +143,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
mutex_lock(&bus->device_lock);
if (!mei_cl_is_connected(cl)) {
- rets = -EBUSY;
+ rets = -ENODEV;
goto out;
}
}
@@ -234,7 +235,7 @@ static void mei_cl_bus_event_work(struct work_struct *work)
/* Prepare for the next read */
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
mutex_lock(&bus->device_lock);
- mei_cl_read_start(cldev->cl, 0, NULL);
+ mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
mutex_unlock(&bus->device_lock);
}
}
@@ -324,7 +325,7 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
mutex_lock(&bus->device_lock);
- ret = mei_cl_read_start(cldev->cl, 0, NULL);
+ ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
mutex_unlock(&bus->device_lock);
if (ret && ret != -EBUSY)
return ret;
@@ -983,12 +984,10 @@ void mei_cl_bus_rescan_work(struct work_struct *work)
container_of(work, struct mei_device, bus_rescan_work);
struct mei_me_client *me_cl;
- mutex_lock(&bus->device_lock);
me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid);
if (me_cl)
mei_amthif_host_init(bus, me_cl);
mei_me_cl_put(me_cl);
- mutex_unlock(&bus->device_lock);
mei_cl_bus_rescan(bus);
}
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index 641c1a5..6fe0235 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -358,8 +358,9 @@ void mei_io_cb_free(struct mei_cl_cb *cb)
*
* Return: mei_cl_cb pointer or NULL;
*/
-struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
- const struct file *fp)
+static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
+ enum mei_cb_file_ops type,
+ const struct file *fp)
{
struct mei_cl_cb *cb;
@@ -420,32 +421,41 @@ static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
}
/**
- * mei_io_cb_alloc_buf - allocate callback buffer
+ * mei_cl_alloc_cb - a convenient wrapper for allocating read cb
*
- * @cb: io callback structure
+ * @cl: host client
* @length: size of the buffer
+ * @type: operation type
+ * @fp: associated file pointer (might be NULL)
*
- * Return: 0 on success
- * -EINVAL if cb is NULL
- * -ENOMEM if allocation failed
+ * Return: cb on success and NULL on failure
*/
-int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length)
+struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
+ enum mei_cb_file_ops fop_type,
+ const struct file *fp)
{
+ struct mei_cl_cb *cb;
+
+ cb = mei_io_cb_init(cl, fop_type, fp);
if (!cb)
- return -EINVAL;
+ return NULL;
if (length == 0)
- return 0;
+ return cb;
cb->buf.data = kmalloc(length, GFP_KERNEL);
- if (!cb->buf.data)
- return -ENOMEM;
+ if (!cb->buf.data) {
+ mei_io_cb_free(cb);
+ return NULL;
+ }
cb->buf.size = length;
- return 0;
+
+ return cb;
}
/**
- * mei_cl_alloc_cb - a convenient wrapper for allocating read cb
+ * mei_cl_enqueue_ctrl_wr_cb - a convenient wrapper for allocating
+ * and enqueuing of the control commands cb
*
* @cl: host client
* @length: size of the buffer
@@ -453,22 +463,23 @@ int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length)
* @fp: associated file pointer (might be NULL)
*
* Return: cb on success and NULL on failure
+ * Locking: called under "dev->device_lock" lock
*/
-struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
- enum mei_cb_file_ops type,
- const struct file *fp)
+struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
+ enum mei_cb_file_ops fop_type,
+ const struct file *fp)
{
struct mei_cl_cb *cb;
- cb = mei_io_cb_init(cl, type, fp);
- if (!cb)
- return NULL;
+ /* for RX always allocate at least client's mtu */
+ if (length)
+ length = max_t(size_t, length, mei_cl_mtu(cl));
- if (mei_io_cb_alloc_buf(cb, length)) {
- mei_io_cb_free(cb);
+ cb = mei_cl_alloc_cb(cl, length, fop_type, fp);
+ if (!cb)
return NULL;
- }
+ list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.list);
return cb;
}
@@ -754,7 +765,8 @@ void mei_cl_set_disconnected(struct mei_cl *cl)
mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl);
mei_cl_wake_all(cl);
- cl->mei_flow_ctrl_creds = 0;
+ cl->rx_flow_ctrl_creds = 0;
+ cl->tx_flow_ctrl_creds = 0;
cl->timer_count = 0;
if (!cl->me_cl)
@@ -764,7 +776,7 @@ void mei_cl_set_disconnected(struct mei_cl *cl)
cl->me_cl->connect_count--;
if (cl->me_cl->connect_count == 0)
- cl->me_cl->mei_flow_ctrl_creds = 0;
+ cl->me_cl->tx_flow_ctrl_creds = 0;
mei_me_cl_put(cl->me_cl);
cl->me_cl = NULL;
@@ -814,6 +826,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
cl->timer_count = MEI_CONNECT_TIMEOUT;
+ mei_schedule_stall_timer(dev);
return 0;
}
@@ -867,13 +880,11 @@ static int __mei_cl_disconnect(struct mei_cl *cl)
cl->state = MEI_FILE_DISCONNECTING;
- cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
- rets = cb ? 0 : -ENOMEM;
- if (rets)
+ cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT, NULL);
+ if (!cb) {
+ rets = -ENOMEM;
goto out;
-
- cl_dbg(dev, cl, "add disconnect cb to control write list\n");
- list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
+ }
if (mei_hbuf_acquire(dev)) {
rets = mei_cl_send_disconnect(cl, cb);
@@ -1001,6 +1012,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
cl->timer_count = MEI_CONNECT_TIMEOUT;
+ mei_schedule_stall_timer(dev);
return 0;
}
@@ -1042,14 +1054,14 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
*
* @cl: host client
* @me_cl: me client
- * @file: pointer to file structure
+ * @fp: pointer to file structure
*
* Locking: called under "dev->device_lock" lock
*
* Return: 0 on success, <0 on failure.
*/
int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
- const struct file *file)
+ const struct file *fp)
{
struct mei_device *dev;
struct mei_cl_cb *cb;
@@ -1076,12 +1088,11 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
goto nortpm;
}
- cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file);
- rets = cb ? 0 : -ENOMEM;
- if (rets)
+ cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_CONNECT, fp);
+ if (!cb) {
+ rets = -ENOMEM;
goto out;
-
- list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
+ }
/* run hbuf acquire last so we don't have to undo */
if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
@@ -1159,50 +1170,42 @@ err:
return ERR_PTR(ret);
}
-
-
/**
- * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
+ * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl.
*
* @cl: host client
- * @fp: the file pointer associated with the pointer
*
- * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
+ * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise.
*/
-static int mei_cl_flow_ctrl_creds(struct mei_cl *cl, const struct file *fp)
+static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl)
{
- int rets;
-
if (WARN_ON(!cl || !cl->me_cl))
return -EINVAL;
- if (cl->mei_flow_ctrl_creds > 0)
+ if (cl->tx_flow_ctrl_creds > 0)
return 1;
- if (mei_cl_is_fixed_address(cl)) {
- rets = mei_cl_read_start(cl, mei_cl_mtu(cl), fp);
- if (rets && rets != -EBUSY)
- return rets;
+ if (mei_cl_is_fixed_address(cl))
return 1;
- }
if (mei_cl_is_single_recv_buf(cl)) {
- if (cl->me_cl->mei_flow_ctrl_creds > 0)
+ if (cl->me_cl->tx_flow_ctrl_creds > 0)
return 1;
}
return 0;
}
/**
- * mei_cl_flow_ctrl_reduce - reduces flow_control.
+ * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits
+ * for a client
*
- * @cl: private data of the file object
+ * @cl: host client
*
* Return:
* 0 on success
* -EINVAL when ctrl credits are <= 0
*/
-static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
+static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl)
{
if (WARN_ON(!cl || !cl->me_cl))
return -EINVAL;
@@ -1211,13 +1214,13 @@ static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
return 0;
if (mei_cl_is_single_recv_buf(cl)) {
- if (WARN_ON(cl->me_cl->mei_flow_ctrl_creds <= 0))
+ if (WARN_ON(cl->me_cl->tx_flow_ctrl_creds <= 0))
return -EINVAL;
- cl->me_cl->mei_flow_ctrl_creds--;
+ cl->me_cl->tx_flow_ctrl_creds--;
} else {
- if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
+ if (WARN_ON(cl->tx_flow_ctrl_creds <= 0))
return -EINVAL;
- cl->mei_flow_ctrl_creds--;
+ cl->tx_flow_ctrl_creds--;
}
return 0;
}
@@ -1292,7 +1295,7 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
* mei_cl_notify_request - send notification stop/start request
*
* @cl: host client
- * @file: associate request with file
+ * @fp: associate request with file
* @request: 1 for start or 0 for stop
*
* Locking: called under "dev->device_lock" lock
@@ -1300,7 +1303,7 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
* Return: 0 on such and error otherwise.
*/
int mei_cl_notify_request(struct mei_cl *cl,
- const struct file *file, u8 request)
+ const struct file *fp, u8 request)
{
struct mei_device *dev;
struct mei_cl_cb *cb;
@@ -1325,7 +1328,7 @@ int mei_cl_notify_request(struct mei_cl *cl,
}
fop_type = mei_cl_notify_req2fop(request);
- cb = mei_io_cb_init(cl, fop_type, file);
+ cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, fop_type, fp);
if (!cb) {
rets = -ENOMEM;
goto out;
@@ -1336,9 +1339,7 @@ int mei_cl_notify_request(struct mei_cl *cl,
rets = -ENODEV;
goto out;
}
- list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
- } else {
- list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
+ list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
}
mutex_unlock(&dev->device_lock);
@@ -1436,25 +1437,6 @@ out:
}
/**
- * mei_cl_is_read_fc_cb - check if read cb is waiting for flow control
- * for given host client
- *
- * @cl: host client
- *
- * Return: true, if found at least one cb.
- */
-static bool mei_cl_is_read_fc_cb(struct mei_cl *cl)
-{
- struct mei_device *dev = cl->dev;
- struct mei_cl_cb *cb;
-
- list_for_each_entry(cb, &dev->ctrl_wr_list.list, list)
- if (cb->fop_type == MEI_FOP_READ && cb->cl == cl)
- return true;
- return false;
-}
-
-/**
* mei_cl_read_start - the start read client message function.
*
* @cl: host client
@@ -1477,26 +1459,22 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
if (!mei_cl_is_connected(cl))
return -ENODEV;
- /* HW currently supports only one pending read */
- if (!list_empty(&cl->rd_pending) || mei_cl_is_read_fc_cb(cl))
- return -EBUSY;
-
if (!mei_me_cl_is_active(cl->me_cl)) {
cl_err(dev, cl, "no such me client\n");
return -ENOTTY;
}
- /* always allocate at least client max message */
- length = max_t(size_t, length, mei_cl_mtu(cl));
- cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
+ if (mei_cl_is_fixed_address(cl) || cl == &dev->iamthif_cl)
+ return 0;
+
+ /* HW currently supports only one pending read */
+ if (cl->rx_flow_ctrl_creds)
+ return -EBUSY;
+
+ cb = mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, fp);
if (!cb)
return -ENOMEM;
- if (mei_cl_is_fixed_address(cl)) {
- list_add_tail(&cb->list, &cl->rd_pending);
- return 0;
- }
-
rets = pm_runtime_get(dev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(dev->dev);
@@ -1504,16 +1482,15 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
goto nortpm;
}
+ rets = 0;
if (mei_hbuf_acquire(dev)) {
rets = mei_hbm_cl_flow_control_req(dev, cl);
if (rets < 0)
goto out;
- list_add_tail(&cb->list, &cl->rd_pending);
- } else {
- rets = 0;
- list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
+ list_move_tail(&cb->list, &cl->rd_pending);
}
+ cl->rx_flow_ctrl_creds++;
out:
cl_dbg(dev, cl, "rpm: autosuspend\n");
@@ -1557,7 +1534,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
first_chunk = cb->buf_idx == 0;
- rets = first_chunk ? mei_cl_flow_ctrl_creds(cl, cb->fp) : 1;
+ rets = first_chunk ? mei_cl_tx_flow_ctrl_creds(cl) : 1;
if (rets < 0)
return rets;
@@ -1605,7 +1582,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
cb->completed = mei_hdr.msg_complete == 1;
if (first_chunk) {
- if (mei_cl_flow_ctrl_reduce(cl))
+ if (mei_cl_tx_flow_ctrl_creds_reduce(cl))
return -EIO;
}
@@ -1663,7 +1640,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
mei_hdr.msg_complete = 0;
mei_hdr.internal = cb->internal;
- rets = mei_cl_flow_ctrl_creds(cl, cb->fp);
+ rets = mei_cl_tx_flow_ctrl_creds(cl);
if (rets < 0)
goto err;
@@ -1691,7 +1668,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
if (rets)
goto err;
- rets = mei_cl_flow_ctrl_reduce(cl);
+ rets = mei_cl_tx_flow_ctrl_creds_reduce(cl);
if (rets)
goto err;
@@ -1761,6 +1738,9 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
case MEI_FOP_READ:
list_add_tail(&cb->list, &cl->rd_completed);
+ if (!mei_cl_is_fixed_address(cl) &&
+ !WARN_ON(!cl->rx_flow_ctrl_creds))
+ cl->rx_flow_ctrl_creds--;
if (!mei_cl_bus_rx_event(cl))
wake_up_interruptible(&cl->rx_wait);
break;
diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h
index 0d7a3a1..d2bfabe 100644
--- a/drivers/misc/mei/client.h
+++ b/drivers/misc/mei/client.h
@@ -82,11 +82,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl)
/*
* MEI IO Functions
*/
-struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
- const struct file *fp);
void mei_io_cb_free(struct mei_cl_cb *priv_cb);
-int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length);
-
/**
* mei_io_list_init - Sets up a queue list.
@@ -118,6 +114,9 @@ void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp);
struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
enum mei_cb_file_ops type,
const struct file *fp);
+struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
+ enum mei_cb_file_ops type,
+ const struct file *fp);
int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp);
/*
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 085f3aa..dd7f15a 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -161,6 +161,7 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
* @dev: the device structure
* @cl: client
* @hbm_cmd: host bus message command
+ * @buf: message buffer
* @len: buffer length
*
* Return: 0 on success, <0 on failure.
@@ -276,6 +277,7 @@ int mei_hbm_start_req(struct mei_device *dev)
dev->hbm_state = MEI_HBM_STARTING;
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
+ mei_schedule_stall_timer(dev);
return 0;
}
@@ -311,6 +313,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev)
}
dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
+ mei_schedule_stall_timer(dev);
return 0;
}
@@ -339,7 +342,7 @@ static int mei_hbm_me_cl_add(struct mei_device *dev,
me_cl->props = res->client_properties;
me_cl->client_id = res->me_addr;
- me_cl->mei_flow_ctrl_creds = 0;
+ me_cl->tx_flow_ctrl_creds = 0;
mei_me_cl_add(dev, me_cl);
@@ -561,6 +564,7 @@ static int mei_hbm_prop_req(struct mei_device *dev, unsigned long start_idx)
}
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
+ mei_schedule_stall_timer(dev);
return 0;
}
@@ -636,23 +640,22 @@ int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
}
/**
- * mei_hbm_add_single_flow_creds - adds single buffer credentials.
+ * mei_hbm_add_single_tx_flow_ctrl_creds - adds single buffer credentials.
*
* @dev: the device structure
- * @flow: flow control.
+ * @fctrl: flow control response bus message
*
* Return: 0 on success, < 0 otherwise
*/
-static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
- struct hbm_flow_control *flow)
+static int mei_hbm_add_single_tx_flow_ctrl_creds(struct mei_device *dev,
+ struct hbm_flow_control *fctrl)
{
struct mei_me_client *me_cl;
int rets;
- me_cl = mei_me_cl_by_id(dev, flow->me_addr);
+ me_cl = mei_me_cl_by_id(dev, fctrl->me_addr);
if (!me_cl) {
- dev_err(dev->dev, "no such me client %d\n",
- flow->me_addr);
+ dev_err(dev->dev, "no such me client %d\n", fctrl->me_addr);
return -ENOENT;
}
@@ -661,9 +664,9 @@ static int mei_hbm_add_single_flow_creds(struct mei_device *dev,
goto out;
}
- me_cl->mei_flow_ctrl_creds++;
+ me_cl->tx_flow_ctrl_creds++;
dev_dbg(dev->dev, "recv flow ctrl msg ME %d (single) creds = %d.\n",
- flow->me_addr, me_cl->mei_flow_ctrl_creds);
+ fctrl->me_addr, me_cl->tx_flow_ctrl_creds);
rets = 0;
out:
@@ -675,24 +678,24 @@ out:
* mei_hbm_cl_flow_control_res - flow control response from me
*
* @dev: the device structure
- * @flow_control: flow control response bus message
+ * @fctrl: flow control response bus message
*/
-static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
- struct hbm_flow_control *flow_control)
+static void mei_hbm_cl_tx_flow_ctrl_creds_res(struct mei_device *dev,
+ struct hbm_flow_control *fctrl)
{
struct mei_cl *cl;
- if (!flow_control->host_addr) {
+ if (!fctrl->host_addr) {
/* single receive buffer */
- mei_hbm_add_single_flow_creds(dev, flow_control);
+ mei_hbm_add_single_tx_flow_ctrl_creds(dev, fctrl);
return;
}
- cl = mei_hbm_cl_find_by_cmd(dev, flow_control);
+ cl = mei_hbm_cl_find_by_cmd(dev, fctrl);
if (cl) {
- cl->mei_flow_ctrl_creds++;
+ cl->tx_flow_ctrl_creds++;
cl_dbg(dev, cl, "flow control creds = %d.\n",
- cl->mei_flow_ctrl_creds);
+ cl->tx_flow_ctrl_creds);
}
}
@@ -871,10 +874,10 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
cl->state = MEI_FILE_DISCONNECTING;
cl->timer_count = 0;
- cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL);
+ cb = mei_cl_enqueue_ctrl_wr_cb(cl, 0, MEI_FOP_DISCONNECT_RSP,
+ NULL);
if (!cb)
return -ENOMEM;
- list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
}
return 0;
}
@@ -1022,7 +1025,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
struct mei_hbm_cl_cmd *cl_cmd;
struct hbm_client_connect_request *disconnect_req;
- struct hbm_flow_control *flow_control;
+ struct hbm_flow_control *fctrl;
/* read the message to our buffer */
BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
@@ -1102,8 +1105,8 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
case MEI_FLOW_CONTROL_CMD:
dev_dbg(dev->dev, "hbm: client flow control response: message received.\n");
- flow_control = (struct hbm_flow_control *) mei_msg;
- mei_hbm_cl_flow_control_res(dev, flow_control);
+ fctrl = (struct hbm_flow_control *)mei_msg;
+ mei_hbm_cl_tx_flow_ctrl_creds_res(dev, fctrl);
break;
case MEI_PG_ISOLATION_ENTRY_RES_CMD:
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index 0dcb854..7ad15d6 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -125,6 +125,9 @@
#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */
#define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */
+#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */
+#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */
+
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index dc3a854..56c2101 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -18,6 +18,7 @@
#include <linux/kthread.h>
#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
#include "mei_dev.h"
#include "hbm.h"
@@ -1063,6 +1064,8 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
}
}
+ pm_runtime_set_active(dev->dev);
+
hcsr = mei_hcsr_read(dev);
/* H_RST may be found lit before reset is started,
* for example if preceding reset flow hasn't completed.
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index 4a6c1b8..e6e5e55 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/irqreturn.h>
+#include <linux/pm_runtime.h>
#include <linux/mei.h>
@@ -935,6 +936,8 @@ static int mei_txe_hw_start(struct mei_device *dev)
return ret;
}
+ pm_runtime_set_active(dev->dev);
+
/* enable input ready interrupts:
* SEC_IPC_HOST_INT_MASK.IPC_INPUT_READY_INT_MASK
*/
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index f7c8dfd..9a9c248 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -94,7 +94,7 @@ void mei_cancel_work(struct mei_device *dev)
cancel_work_sync(&dev->reset_work);
cancel_work_sync(&dev->bus_rescan_work);
- cancel_delayed_work(&dev->timer_work);
+ cancel_delayed_work_sync(&dev->timer_work);
}
EXPORT_SYMBOL_GPL(mei_cancel_work);
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index 3831a7b..5a4893c 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -102,26 +102,25 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
{
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
- unsigned char *buffer = NULL;
size_t buf_sz;
cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
if (!cb) {
- cl_err(dev, cl, "pending read cb not found\n");
- goto out;
+ if (!mei_cl_is_fixed_address(cl)) {
+ cl_err(dev, cl, "pending read cb not found\n");
+ goto discard;
+ }
+ cb = mei_cl_alloc_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, cl->fp);
+ if (!cb)
+ goto discard;
+ list_add_tail(&cb->list, &cl->rd_pending);
}
if (!mei_cl_is_connected(cl)) {
cl_dbg(dev, cl, "not connected\n");
- cb->status = -ENODEV;
- goto out;
- }
-
- if (cb->buf.size == 0 || cb->buf.data == NULL) {
- cl_err(dev, cl, "response buffer is not allocated.\n");
list_move_tail(&cb->list, &complete_list->list);
- cb->status = -ENOMEM;
- goto out;
+ cb->status = -ENODEV;
+ goto discard;
}
buf_sz = mei_hdr->length + cb->buf_idx;
@@ -132,25 +131,19 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
list_move_tail(&cb->list, &complete_list->list);
cb->status = -EMSGSIZE;
- goto out;
+ goto discard;
}
if (cb->buf.size < buf_sz) {
cl_dbg(dev, cl, "message overflow. size %zu len %d idx %zu\n",
cb->buf.size, mei_hdr->length, cb->buf_idx);
- buffer = krealloc(cb->buf.data, buf_sz, GFP_KERNEL);
- if (!buffer) {
- cb->status = -ENOMEM;
- list_move_tail(&cb->list, &complete_list->list);
- goto out;
- }
- cb->buf.data = buffer;
- cb->buf.size = buf_sz;
+ list_move_tail(&cb->list, &complete_list->list);
+ cb->status = -EMSGSIZE;
+ goto discard;
}
- buffer = cb->buf.data + cb->buf_idx;
- mei_read_slots(dev, buffer, mei_hdr->length);
+ mei_read_slots(dev, cb->buf.data + cb->buf_idx, mei_hdr->length);
cb->buf_idx += mei_hdr->length;
@@ -162,10 +155,10 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
pm_request_autosuspend(dev->dev);
}
-out:
- if (!buffer)
- mei_irq_discard_msg(dev, mei_hdr);
+ return 0;
+discard:
+ mei_irq_discard_msg(dev, mei_hdr);
return 0;
}
@@ -216,6 +209,9 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
int slots;
int ret;
+ if (!list_empty(&cl->rd_pending))
+ return 0;
+
msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
slots = mei_hbuf_empty_slots(dev);
@@ -463,6 +459,19 @@ static void mei_connect_timeout(struct mei_cl *cl)
mei_reset(dev);
}
+#define MEI_STALL_TIMER_FREQ (2 * HZ)
+/**
+ * mei_schedule_stall_timer - re-arm stall_timer work
+ *
+ * Schedule stall timer
+ *
+ * @dev: the device structure
+ */
+void mei_schedule_stall_timer(struct mei_device *dev)
+{
+ schedule_delayed_work(&dev->timer_work, MEI_STALL_TIMER_FREQ);
+}
+
/**
* mei_timer - timer function.
*
@@ -472,10 +481,9 @@ static void mei_connect_timeout(struct mei_cl *cl)
void mei_timer(struct work_struct *work)
{
struct mei_cl *cl;
-
struct mei_device *dev = container_of(work,
struct mei_device, timer_work.work);
-
+ bool reschedule_timer = false;
mutex_lock(&dev->device_lock);
@@ -490,6 +498,7 @@ void mei_timer(struct work_struct *work)
mei_reset(dev);
goto out;
}
+ reschedule_timer = true;
}
}
@@ -504,6 +513,7 @@ void mei_timer(struct work_struct *work)
mei_connect_timeout(cl);
goto out;
}
+ reschedule_timer = true;
}
}
@@ -514,19 +524,16 @@ void mei_timer(struct work_struct *work)
if (--dev->iamthif_stall_timer == 0) {
dev_err(dev->dev, "timer: amthif hanged.\n");
mei_reset(dev);
- dev->iamthif_canceled = false;
- dev->iamthif_state = MEI_IAMTHIF_IDLE;
- mei_io_cb_free(dev->iamthif_current_cb);
- dev->iamthif_current_cb = NULL;
-
- dev->iamthif_fp = NULL;
mei_amthif_run_next_cmd(dev);
+ goto out;
}
+ reschedule_timer = true;
}
out:
- if (dev->dev_state != MEI_DEV_DISABLED)
- schedule_delayed_work(&dev->timer_work, 2 * HZ);
+ if (dev->dev_state != MEI_DEV_DISABLED && reschedule_timer)
+ mei_schedule_stall_timer(dev);
+
mutex_unlock(&dev->device_lock);
}
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 52635b0..fa50635 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -71,6 +71,7 @@ static int mei_open(struct inode *inode, struct file *file)
goto err_unlock;
}
+ cl->fp = file;
file->private_data = cl;
mutex_unlock(&dev->device_lock);
@@ -138,9 +139,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
struct mei_cl *cl = file->private_data;
struct mei_device *dev;
struct mei_cl_cb *cb = NULL;
+ bool nonblock = !!(file->f_flags & O_NONBLOCK);
int rets;
- int err;
-
if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
@@ -164,11 +164,6 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
goto out;
}
- if (cl == &dev->iamthif_cl) {
- rets = mei_amthif_read(dev, file, ubuf, length, offset);
- goto out;
- }
-
cb = mei_cl_read_cb(cl, file);
if (cb)
goto copy_buffer;
@@ -176,24 +171,29 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
if (*offset > 0)
*offset = 0;
- err = mei_cl_read_start(cl, length, file);
- if (err && err != -EBUSY) {
- cl_dbg(dev, cl, "mei start read failure status = %d\n", err);
- rets = err;
+ rets = mei_cl_read_start(cl, length, file);
+ if (rets && rets != -EBUSY) {
+ cl_dbg(dev, cl, "mei start read failure status = %d\n", rets);
goto out;
}
- if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
- if (file->f_flags & O_NONBLOCK) {
- rets = -EAGAIN;
- goto out;
- }
+ if (nonblock) {
+ rets = -EAGAIN;
+ goto out;
+ }
+ if (rets == -EBUSY &&
+ !mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, file)) {
+ rets = -ENOMEM;
+ goto out;
+ }
+
+ do {
mutex_unlock(&dev->device_lock);
if (wait_event_interruptible(cl->rx_wait,
- (!list_empty(&cl->rd_completed)) ||
- (!mei_cl_is_connected(cl)))) {
+ (!list_empty(&cl->rd_completed)) ||
+ (!mei_cl_is_connected(cl)))) {
if (signal_pending(current))
return -EINTR;
@@ -202,16 +202,12 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) {
- rets = -EBUSY;
+ rets = -ENODEV;
goto out;
}
- }
- cb = mei_cl_read_cb(cl, file);
- if (!cb) {
- rets = 0;
- goto out;
- }
+ cb = mei_cl_read_cb(cl, file);
+ } while (!cb);
copy_buffer:
/* now copy the data to user space */
@@ -609,24 +605,24 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
goto out;
}
- if (cl == &dev->iamthif_cl) {
- mask = mei_amthif_poll(dev, file, wait);
- goto out;
- }
-
if (notify_en) {
poll_wait(file, &cl->ev_wait, wait);
if (cl->notify_ev)
mask |= POLLPRI;
}
+ if (cl == &dev->iamthif_cl) {
+ mask |= mei_amthif_poll(file, wait);
+ goto out;
+ }
+
if (req_events & (POLLIN | POLLRDNORM)) {
poll_wait(file, &cl->rx_wait, wait);
if (!list_empty(&cl->rd_completed))
mask |= POLLIN | POLLRDNORM;
else
- mei_cl_read_start(cl, 0, file);
+ mei_cl_read_start(cl, mei_cl_mtu(cl), file);
}
out:
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index e5e3250..1169fd9 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -80,18 +80,13 @@ const char *mei_dev_state_str(int state);
enum iamthif_states {
MEI_IAMTHIF_IDLE,
MEI_IAMTHIF_WRITING,
- MEI_IAMTHIF_FLOW_CONTROL,
MEI_IAMTHIF_READING,
- MEI_IAMTHIF_READ_COMPLETE
};
enum mei_file_transaction_states {
MEI_IDLE,
MEI_WRITING,
MEI_WRITE_COMPLETE,
- MEI_FLOW_CONTROL,
- MEI_READING,
- MEI_READ_COMPLETE
};
/**
@@ -146,7 +141,7 @@ struct mei_fw_status {
* @refcnt: struct reference count
* @props: client properties
* @client_id: me client id
- * @mei_flow_ctrl_creds: flow control credits
+ * @tx_flow_ctrl_creds: flow control credits
* @connect_count: number connections to this client
* @bus_added: added to bus
*/
@@ -155,7 +150,7 @@ struct mei_me_client {
struct kref refcnt;
struct mei_client_properties props;
u8 client_id;
- u8 mei_flow_ctrl_creds;
+ u8 tx_flow_ctrl_creds;
u8 connect_count;
u8 bus_added;
};
@@ -202,10 +197,11 @@ struct mei_cl_cb {
* @ev_async: event async notification
* @status: connection status
* @me_cl: fw client connected
+ * @fp: file associated with client
* @host_client_id: host id
- * @mei_flow_ctrl_creds: transmit flow credentials
+ * @tx_flow_ctrl_creds: transmit flow credentials
+ * @rx_flow_ctrl_creds: receive flow credentials
* @timer_count: watchdog timer for operation completion
- * @reserved: reserved for alignment
* @notify_en: notification - enabled/disabled
* @notify_ev: pending notification event
* @writing_state: state of the tx
@@ -225,10 +221,11 @@ struct mei_cl {
struct fasync_struct *ev_async;
int status;
struct mei_me_client *me_cl;
+ const struct file *fp;
u8 host_client_id;
- u8 mei_flow_ctrl_creds;
+ u8 tx_flow_ctrl_creds;
+ u8 rx_flow_ctrl_creds;
u8 timer_count;
- u8 reserved;
u8 notify_en;
u8 notify_ev;
enum mei_file_transaction_states writing_state;
@@ -400,9 +397,7 @@ const char *mei_pg_state_str(enum mei_pg_state state);
* @override_fixed_address: force allow fixed address behavior
*
* @amthif_cmd_list : amthif list for cmd waiting
- * @iamthif_fp : file for current amthif operation
* @iamthif_cl : amthif host client
- * @iamthif_current_cb : amthif current operation callback
* @iamthif_open_count : number of opened amthif connections
* @iamthif_stall_timer : timer to detect amthif hang
* @iamthif_state : amthif processor state
@@ -484,10 +479,7 @@ struct mei_device {
/* amthif list for cmd waiting */
struct mei_cl_cb amthif_cmd_list;
- /* driver managed amthif list for reading completed amthif cmd data */
- const struct file *iamthif_fp;
struct mei_cl iamthif_cl;
- struct mei_cl_cb *iamthif_current_cb;
long iamthif_open_count;
u32 iamthif_stall_timer;
enum iamthif_states iamthif_state;
@@ -556,6 +548,7 @@ void mei_cancel_work(struct mei_device *dev);
*/
void mei_timer(struct work_struct *work);
+void mei_schedule_stall_timer(struct mei_device *dev);
int mei_irq_read_handler(struct mei_device *dev,
struct mei_cl_cb *cmpl_list, s32 *slots);
@@ -569,11 +562,7 @@ void mei_amthif_reset_params(struct mei_device *dev);
int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl);
-int mei_amthif_read(struct mei_device *dev, struct file *file,
- char __user *ubuf, size_t length, loff_t *offset);
-
-unsigned int mei_amthif_poll(struct mei_device *dev,
- struct file *file, poll_table *wait);
+unsigned int mei_amthif_poll(struct file *file, poll_table *wait);
int mei_amthif_release(struct mei_device *dev, struct file *file);
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 71cea9b..f3ffd88 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -91,6 +91,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, mei_me_pch8_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)},
+
/* required last entry */
{0, }
};
@@ -217,8 +220,6 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, dev);
- schedule_delayed_work(&dev->timer_work, HZ);
-
/*
* For not wake-able HW runtime pm framework
* can't be used on pci device level.
@@ -400,6 +401,9 @@ static int mei_me_pm_runtime_suspend(struct device *device)
dev_dbg(&pdev->dev, "rpm: me: runtime suspend ret=%d\n", ret);
+ if (ret && ret != -EAGAIN)
+ schedule_work(&dev->reset_work);
+
return ret;
}
@@ -423,6 +427,9 @@ static int mei_me_pm_runtime_resume(struct device *device)
dev_dbg(&pdev->dev, "rpm: me: runtime resume ret = %d\n", ret);
+ if (ret)
+ schedule_work(&dev->reset_work);
+
return ret;
}
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index 30cc306..58ffd30 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -347,6 +347,10 @@ static int mei_txe_pm_runtime_suspend(struct device *device)
dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
mutex_unlock(&dev->device_lock);
+
+ if (ret && ret != -EAGAIN)
+ schedule_work(&dev->reset_work);
+
return ret;
}
@@ -372,6 +376,9 @@ static int mei_txe_pm_runtime_resume(struct device *device)
dev_dbg(&pdev->dev, "rpm: txe: runtime resume ret = %d\n", ret);
+ if (ret)
+ schedule_work(&dev->reset_work);
+
return ret;
}
diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c
index cd01a0e..64d5760 100644
--- a/drivers/misc/mic/scif/scif_dma.c
+++ b/drivers/misc/mic/scif/scif_dma.c
@@ -115,7 +115,6 @@ int scif_reserve_dma_chan(struct scif_endpt *ep)
*/
static
void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn,
- struct scif_endpt *ep,
u64 start, u64 len)
{
struct list_head *item, *tmp;
@@ -128,7 +127,6 @@ void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn,
list_for_each_safe(item, tmp, &mmn->tc_reg_list) {
window = list_entry(item, struct scif_window, list);
- ep = (struct scif_endpt *)window->ep;
if (!len)
break;
start_va = window->va_for_temp;
@@ -146,7 +144,7 @@ static void scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, u64 start, u64 len)
struct scif_endpt *ep = mmn->ep;
spin_lock(&ep->rma_info.tc_lock);
- __scif_rma_destroy_tcw(mmn, ep, start, len);
+ __scif_rma_destroy_tcw(mmn, start, len);
spin_unlock(&ep->rma_info.tc_lock);
}
@@ -169,7 +167,7 @@ static void __scif_rma_destroy_tcw_ep(struct scif_endpt *ep)
spin_lock(&ep->rma_info.tc_lock);
list_for_each_safe(item, tmp, &ep->rma_info.mmn_list) {
mmn = list_entry(item, struct scif_mmu_notif, list);
- __scif_rma_destroy_tcw(mmn, ep, 0, ULONG_MAX);
+ __scif_rma_destroy_tcw(mmn, 0, ULONG_MAX);
}
spin_unlock(&ep->rma_info.tc_lock);
}
diff --git a/drivers/misc/mic/scif/scif_mmap.c b/drivers/misc/mic/scif/scif_mmap.c
index 49cb8f7..9282116 100644
--- a/drivers/misc/mic/scif/scif_mmap.c
+++ b/drivers/misc/mic/scif/scif_mmap.c
@@ -552,7 +552,7 @@ static void scif_munmap(struct vm_area_struct *vma)
{
struct scif_endpt *ep;
struct vma_pvt *vmapvt = vma->vm_private_data;
- int nr_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ int nr_pages = vma_pages(vma);
s64 offset;
struct scif_rma_req req;
struct scif_window *window = NULL;
@@ -614,7 +614,7 @@ int scif_mmap(struct vm_area_struct *vma, scif_epd_t epd)
struct scif_window *window = NULL;
struct scif_endpt *ep = (struct scif_endpt *)epd;
s64 start_offset = vma->vm_pgoff << PAGE_SHIFT;
- int nr_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ int nr_pages = vma_pages(vma);
int err;
struct vma_pvt *vmapvt;
diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c
index 4810e03..e42bdc9 100644
--- a/drivers/misc/pch_phub.c
+++ b/drivers/misc/pch_phub.c
@@ -28,6 +28,7 @@
#include <linux/if_ether.h>
#include <linux/ctype.h>
#include <linux/dmi.h>
+#include <linux/of.h>
#define PHUB_STATUS 0x00 /* Status Register offset */
#define PHUB_CONTROL 0x04 /* Control Register offset */
@@ -57,6 +58,7 @@
/* CM-iTC */
#define CLKCFG_UART_48MHZ (1 << 16)
+#define CLKCFG_UART_25MHZ (2 << 16)
#define CLKCFG_BAUDDIV (2 << 20)
#define CLKCFG_PLL2VCO (8 << 9)
#define CLKCFG_UARTCLKSEL (1 << 18)
@@ -711,6 +713,12 @@ static int pch_phub_probe(struct pci_dev *pdev,
if (id->driver_data == 1) { /* EG20T PCH */
const char *board_name;
+ unsigned int prefetch = 0x000affaa;
+
+ if (pdev->dev.of_node)
+ of_property_read_u32(pdev->dev.of_node,
+ "intel,eg20t-prefetch",
+ &prefetch);
ret = sysfs_create_file(&pdev->dev.kobj,
&dev_attr_pch_mac.attr);
@@ -736,11 +744,21 @@ static int pch_phub_probe(struct pci_dev *pdev,
CLKCFG_UART_MASK);
/* set the prefech value */
- iowrite32(0x000affaa, chip->pch_phub_base_address + 0x14);
+ iowrite32(prefetch, chip->pch_phub_base_address + 0x14);
/* set the interrupt delay value */
iowrite32(0x25, chip->pch_phub_base_address + 0x44);
chip->pch_opt_rom_start_address = PCH_PHUB_ROM_START_ADDR_EG20T;
chip->pch_mac_start_address = PCH_PHUB_MAC_START_ADDR_EG20T;
+
+ /* quirk for MIPS Boston platform */
+ if (pdev->dev.of_node) {
+ if (of_machine_is_compatible("img,boston")) {
+ pch_phub_read_modify_write_reg(chip,
+ (unsigned int)CLKCFG_REG_OFFSET,
+ CLKCFG_UART_25MHZ,
+ CLKCFG_UART_MASK);
+ }
+ }
} else if (id->driver_data == 2) { /* ML7213 IOH */
ret = sysfs_create_bin_file(&pdev->dev.kobj, &pch_bin_attr);
if (ret)
diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c
index 9ec262a..ec09010 100644
--- a/drivers/misc/vmw_vmci/vmci_host.c
+++ b/drivers/misc/vmw_vmci/vmci_host.c
@@ -381,18 +381,12 @@ static int vmci_host_do_send_datagram(struct vmci_host_dev *vmci_host_dev,
return -EINVAL;
}
- dg = kmalloc(send_info.len, GFP_KERNEL);
- if (!dg) {
+ dg = memdup_user((void __user *)(uintptr_t)send_info.addr,
+ send_info.len);
+ if (IS_ERR(dg)) {
vmci_ioctl_err(
"cannot allocate memory to dispatch datagram\n");
- return -ENOMEM;
- }
-
- if (copy_from_user(dg, (void __user *)(uintptr_t)send_info.addr,
- send_info.len)) {
- vmci_ioctl_err("error getting datagram\n");
- kfree(dg);
- return -EFAULT;
+ return PTR_ERR(dg);
}
if (VMCI_DG_SIZE(dg) != send_info.len) {
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index 1e819f9..bb3e0d1 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -2116,13 +2116,11 @@ static int vub300_probe(struct usb_interface *interface,
command_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!command_out_urb) {
retval = -ENOMEM;
- dev_err(&udev->dev, "not enough memory for command_out_urb\n");
goto error0;
}
command_res_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!command_res_urb) {
retval = -ENOMEM;
- dev_err(&udev->dev, "not enough memory for command_res_urb\n");
goto error1;
}
/* this also allocates memory for our VUB300 mmc host device */
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index cc07ba0..27fa8b8 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -240,6 +240,9 @@ static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode)
unsigned long flags;
u32 val;
+ /* Reset ECC hardware */
+ davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
+
spin_lock_irqsave(&davinci_nand_lock, flags);
/* Start 4-bit ECC calculation for read/write */
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index a59361c..5513bfd9 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -2169,7 +2169,7 @@ scan_tail:
return 0;
return_error:
- if (info->dma)
+ if (!IS_ERR_OR_NULL(info->dma))
dma_release_channel(info->dma);
if (nand_chip->ecc.priv) {
nand_bch_free(nand_chip->ecc.priv);
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index e21f7cc..8d6208c 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/workqueue.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/skb.h>
@@ -501,9 +502,8 @@ EXPORT_SYMBOL_GPL(can_free_echo_skb);
/*
* CAN device restart for bus-off recovery
*/
-static void can_restart(unsigned long data)
+static void can_restart(struct net_device *dev)
{
- struct net_device *dev = (struct net_device *)data;
struct can_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
struct sk_buff *skb;
@@ -543,6 +543,14 @@ restart:
netdev_err(dev, "Error %d during restart", err);
}
+static void can_restart_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct can_priv *priv = container_of(dwork, struct can_priv, restart_work);
+
+ can_restart(priv->dev);
+}
+
int can_restart_now(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
@@ -556,8 +564,8 @@ int can_restart_now(struct net_device *dev)
if (priv->state != CAN_STATE_BUS_OFF)
return -EBUSY;
- /* Runs as soon as possible in the timer context */
- mod_timer(&priv->restart_timer, jiffies);
+ cancel_delayed_work_sync(&priv->restart_work);
+ can_restart(dev);
return 0;
}
@@ -578,8 +586,8 @@ void can_bus_off(struct net_device *dev)
netif_carrier_off(dev);
if (priv->restart_ms)
- mod_timer(&priv->restart_timer,
- jiffies + (priv->restart_ms * HZ) / 1000);
+ schedule_delayed_work(&priv->restart_work,
+ msecs_to_jiffies(priv->restart_ms));
}
EXPORT_SYMBOL_GPL(can_bus_off);
@@ -688,6 +696,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
return NULL;
priv = netdev_priv(dev);
+ priv->dev = dev;
if (echo_skb_max) {
priv->echo_skb_max = echo_skb_max;
@@ -697,7 +706,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
priv->state = CAN_STATE_STOPPED;
- init_timer(&priv->restart_timer);
+ INIT_DELAYED_WORK(&priv->restart_work, can_restart_work);
return dev;
}
@@ -778,8 +787,6 @@ int open_candev(struct net_device *dev)
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
- setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev);
-
return 0;
}
EXPORT_SYMBOL_GPL(open_candev);
@@ -794,7 +801,7 @@ void close_candev(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
- del_timer_sync(&priv->restart_timer);
+ cancel_delayed_work_sync(&priv->restart_work);
can_flush_echo_skb(dev);
}
EXPORT_SYMBOL_GPL(close_candev);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 8d4f849..5414563 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -453,25 +453,29 @@ static inline void bcmgenet_rdma_ring_writel(struct bcmgenet_priv *priv,
static int bcmgenet_get_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
if (!netif_running(dev))
return -EINVAL;
- if (!dev->phydev)
+ if (!priv->phydev)
return -ENODEV;
- return phy_ethtool_gset(dev->phydev, cmd);
+ return phy_ethtool_gset(priv->phydev, cmd);
}
static int bcmgenet_set_settings(struct net_device *dev,
struct ethtool_cmd *cmd)
{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
if (!netif_running(dev))
return -EINVAL;
- if (!dev->phydev)
+ if (!priv->phydev)
return -ENODEV;
- return phy_ethtool_sset(dev->phydev, cmd);
+ return phy_ethtool_sset(priv->phydev, cmd);
}
static int bcmgenet_set_rx_csum(struct net_device *dev,
@@ -937,7 +941,7 @@ static int bcmgenet_get_eee(struct net_device *dev, struct ethtool_eee *e)
e->eee_active = p->eee_active;
e->tx_lpi_timer = bcmgenet_umac_readl(priv, UMAC_EEE_LPI_TIMER);
- return phy_ethtool_get_eee(dev->phydev, e);
+ return phy_ethtool_get_eee(priv->phydev, e);
}
static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e)
@@ -954,7 +958,7 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e)
if (!p->eee_enabled) {
bcmgenet_eee_enable_set(dev, false);
} else {
- ret = phy_init_eee(dev->phydev, 0);
+ ret = phy_init_eee(priv->phydev, 0);
if (ret) {
netif_err(priv, hw, dev, "EEE initialization failed\n");
return ret;
@@ -964,12 +968,14 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e)
bcmgenet_eee_enable_set(dev, true);
}
- return phy_ethtool_set_eee(dev->phydev, e);
+ return phy_ethtool_set_eee(priv->phydev, e);
}
static int bcmgenet_nway_reset(struct net_device *dev)
{
- return genphy_restart_aneg(dev->phydev);
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ return genphy_restart_aneg(priv->phydev);
}
/* standard ethtool support functions. */
@@ -996,13 +1002,12 @@ static struct ethtool_ops bcmgenet_ethtool_ops = {
static int bcmgenet_power_down(struct bcmgenet_priv *priv,
enum bcmgenet_power_mode mode)
{
- struct net_device *ndev = priv->dev;
int ret = 0;
u32 reg;
switch (mode) {
case GENET_POWER_CABLE_SENSE:
- phy_detach(ndev->phydev);
+ phy_detach(priv->phydev);
break;
case GENET_POWER_WOL_MAGIC:
@@ -1063,6 +1068,7 @@ static void bcmgenet_power_up(struct bcmgenet_priv *priv,
/* ioctl handle special commands that are not present in ethtool. */
static int bcmgenet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
int val = 0;
if (!netif_running(dev))
@@ -1072,10 +1078,10 @@ static int bcmgenet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSMIIREG:
- if (!dev->phydev)
+ if (!priv->phydev)
val = -ENODEV;
else
- val = phy_mii_ioctl(dev->phydev, rq, cmd);
+ val = phy_mii_ioctl(priv->phydev, rq, cmd);
break;
default:
@@ -2458,7 +2464,6 @@ static void bcmgenet_irq_task(struct work_struct *work)
{
struct bcmgenet_priv *priv = container_of(
work, struct bcmgenet_priv, bcmgenet_irq_work);
- struct net_device *ndev = priv->dev;
netif_dbg(priv, intr, priv->dev, "%s\n", __func__);
@@ -2471,7 +2476,7 @@ static void bcmgenet_irq_task(struct work_struct *work)
/* Link UP/DOWN event */
if (priv->irq0_stat & UMAC_IRQ_LINK_EVENT) {
- phy_mac_interrupt(ndev->phydev,
+ phy_mac_interrupt(priv->phydev,
!!(priv->irq0_stat & UMAC_IRQ_LINK_UP));
priv->irq0_stat &= ~UMAC_IRQ_LINK_EVENT;
}
@@ -2833,7 +2838,7 @@ static void bcmgenet_netif_start(struct net_device *dev)
/* Monitor link interrupts now */
bcmgenet_link_intr_enable(priv);
- phy_start(dev->phydev);
+ phy_start(priv->phydev);
}
static int bcmgenet_open(struct net_device *dev)
@@ -2932,7 +2937,7 @@ static void bcmgenet_netif_stop(struct net_device *dev)
struct bcmgenet_priv *priv = netdev_priv(dev);
netif_tx_stop_all_queues(dev);
- phy_stop(dev->phydev);
+ phy_stop(priv->phydev);
bcmgenet_intr_disable(priv);
bcmgenet_disable_rx_napi(priv);
bcmgenet_disable_tx_napi(priv);
@@ -2958,7 +2963,7 @@ static int bcmgenet_close(struct net_device *dev)
bcmgenet_netif_stop(dev);
/* Really kill the PHY state machine and disconnect from it */
- phy_disconnect(dev->phydev);
+ phy_disconnect(priv->phydev);
/* Disable MAC receive */
umac_enable_set(priv, CMD_RX_EN, false);
@@ -3517,7 +3522,7 @@ static int bcmgenet_suspend(struct device *d)
bcmgenet_netif_stop(dev);
- phy_suspend(dev->phydev);
+ phy_suspend(priv->phydev);
netif_device_detach(dev);
@@ -3581,7 +3586,7 @@ static int bcmgenet_resume(struct device *d)
if (priv->wolopts)
clk_disable_unprepare(priv->clk_wol);
- phy_init_hw(dev->phydev);
+ phy_init_hw(priv->phydev);
/* Speed settings must be restored */
bcmgenet_mii_config(priv->dev);
@@ -3614,7 +3619,7 @@ static int bcmgenet_resume(struct device *d)
netif_device_attach(dev);
- phy_resume(dev->phydev);
+ phy_resume(priv->phydev);
if (priv->eee.eee_enabled)
bcmgenet_eee_enable_set(dev, true);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
index 0f0868c..1e2dc34 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
@@ -597,6 +597,7 @@ struct bcmgenet_priv {
/* MDIO bus variables */
wait_queue_head_t wq;
+ struct phy_device *phydev;
bool internal_phy;
struct device_node *phy_dn;
struct device_node *mdio_dn;
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index e907acd..457c3bc 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -86,7 +86,7 @@ static int bcmgenet_mii_write(struct mii_bus *bus, int phy_id,
void bcmgenet_mii_setup(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
- struct phy_device *phydev = dev->phydev;
+ struct phy_device *phydev = priv->phydev;
u32 reg, cmd_bits = 0;
bool status_changed = false;
@@ -183,9 +183,9 @@ void bcmgenet_mii_reset(struct net_device *dev)
if (GENET_IS_V4(priv))
return;
- if (dev->phydev) {
- phy_init_hw(dev->phydev);
- phy_start_aneg(dev->phydev);
+ if (priv->phydev) {
+ phy_init_hw(priv->phydev);
+ phy_start_aneg(priv->phydev);
}
}
@@ -236,7 +236,6 @@ static void bcmgenet_internal_phy_setup(struct net_device *dev)
static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
{
- struct net_device *ndev = priv->dev;
u32 reg;
/* Speed settings are set in bcmgenet_mii_setup() */
@@ -245,14 +244,14 @@ static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL);
if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET)
- fixed_phy_set_link_update(ndev->phydev,
+ fixed_phy_set_link_update(priv->phydev,
bcmgenet_fixed_phy_link_update);
}
int bcmgenet_mii_config(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
- struct phy_device *phydev = dev->phydev;
+ struct phy_device *phydev = priv->phydev;
struct device *kdev = &priv->pdev->dev;
const char *phy_name = NULL;
u32 id_mode_dis = 0;
@@ -303,7 +302,7 @@ int bcmgenet_mii_config(struct net_device *dev)
* capabilities, use that knowledge to also configure the
* Reverse MII interface correctly.
*/
- if ((phydev->supported & PHY_BASIC_FEATURES) ==
+ if ((priv->phydev->supported & PHY_BASIC_FEATURES) ==
PHY_BASIC_FEATURES)
port_ctrl = PORT_MODE_EXT_RVMII_25;
else
@@ -372,7 +371,7 @@ int bcmgenet_mii_probe(struct net_device *dev)
return -ENODEV;
}
} else {
- phydev = dev->phydev;
+ phydev = priv->phydev;
phydev->dev_flags = phy_flags;
ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup,
@@ -383,6 +382,8 @@ int bcmgenet_mii_probe(struct net_device *dev)
}
}
+ priv->phydev = phydev;
+
/* Configure port multiplexer based on what the probed PHY device since
* reading the 'max-speed' property determines the maximum supported
* PHY speed which is needed for bcmgenet_mii_config() to configure
@@ -390,7 +391,7 @@ int bcmgenet_mii_probe(struct net_device *dev)
*/
ret = bcmgenet_mii_config(dev);
if (ret) {
- phy_disconnect(phydev);
+ phy_disconnect(priv->phydev);
return ret;
}
@@ -400,7 +401,7 @@ int bcmgenet_mii_probe(struct net_device *dev)
* Ethernet MAC ISRs
*/
if (priv->internal_phy)
- phydev->irq = PHY_IGNORE_INTERRUPT;
+ priv->phydev->irq = PHY_IGNORE_INTERRUPT;
return 0;
}
@@ -605,6 +606,7 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
}
+ priv->phydev = phydev;
priv->phy_interface = pd->phy_interface;
return 0;
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index a2551bc..ea967df 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -18122,14 +18122,14 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
rtnl_lock();
- /* We needn't recover from permanent error */
- if (state == pci_channel_io_frozen)
- tp->pcierr_recovery = true;
-
/* We probably don't have netdev yet */
if (!netdev || !netif_running(netdev))
goto done;
+ /* We needn't recover from permanent error */
+ if (state == pci_channel_io_frozen)
+ tp->pcierr_recovery = true;
+
tg3_phy_stop(tp);
tg3_netif_stop(tp);
@@ -18226,7 +18226,7 @@ static void tg3_io_resume(struct pci_dev *pdev)
rtnl_lock();
- if (!netif_running(netdev))
+ if (!netdev || !netif_running(netdev))
goto done;
tg3_full_lock(tp, 0);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 01f7e81..692ee24 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -89,10 +89,10 @@ static struct platform_device_id fec_devtype[] = {
.driver_data = 0,
}, {
.name = "imx25-fec",
- .driver_data = FEC_QUIRK_USE_GASKET | FEC_QUIRK_HAS_RACC,
+ .driver_data = FEC_QUIRK_USE_GASKET,
}, {
.name = "imx27-fec",
- .driver_data = FEC_QUIRK_HAS_RACC,
+ .driver_data = 0,
}, {
.name = "imx28-fec",
.driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
@@ -180,6 +180,7 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
/* FEC receive acceleration */
#define FEC_RACC_IPDIS (1 << 1)
#define FEC_RACC_PRODIS (1 << 2)
+#define FEC_RACC_SHIFT16 BIT(7)
#define FEC_RACC_OPTIONS (FEC_RACC_IPDIS | FEC_RACC_PRODIS)
/*
@@ -945,9 +946,11 @@ fec_restart(struct net_device *ndev)
#if !defined(CONFIG_M5272)
if (fep->quirks & FEC_QUIRK_HAS_RACC) {
- /* set RX checksum */
val = readl(fep->hwp + FEC_RACC);
+ /* align IP header */
+ val |= FEC_RACC_SHIFT16;
if (fep->csum_flags & FLAG_RX_CSUM_ENABLED)
+ /* set RX checksum */
val |= FEC_RACC_OPTIONS;
else
val &= ~FEC_RACC_OPTIONS;
@@ -1428,6 +1431,12 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
prefetch(skb->data - NET_IP_ALIGN);
skb_put(skb, pkt_len - 4);
data = skb->data;
+
+#if !defined(CONFIG_M5272)
+ if (fep->quirks & FEC_QUIRK_HAS_RACC)
+ data = skb_pull_inline(skb, 2);
+#endif
+
if (!is_copybreak && need_swap)
swap_buffer(data, pkt_len);
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index d41c28d..b745487 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -382,7 +382,8 @@ struct mvneta_port {
struct mvneta_rx_queue *rxqs;
struct mvneta_tx_queue *txqs;
struct net_device *dev;
- struct notifier_block cpu_notifier;
+ struct hlist_node node_online;
+ struct hlist_node node_dead;
int rxq_def;
/* Protect the access to the percpu interrupt registers,
* ensuring that the configuration remains coherent.
@@ -574,6 +575,7 @@ struct mvneta_rx_queue {
int next_desc_to_proc;
};
+static enum cpuhp_state online_hpstate;
/* The hardware supports eight (8) rx queues, but we are only allowing
* the first one to be used. Therefore, let's just allocate one queue.
*/
@@ -3311,101 +3313,104 @@ static void mvneta_percpu_elect(struct mvneta_port *pp)
}
};
-static int mvneta_percpu_notifier(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
+static int mvneta_cpu_online(unsigned int cpu, struct hlist_node *node)
{
- struct mvneta_port *pp = container_of(nfb, struct mvneta_port,
- cpu_notifier);
- int cpu = (unsigned long)hcpu, other_cpu;
+ int other_cpu;
+ struct mvneta_port *pp = hlist_entry_safe(node, struct mvneta_port,
+ node_online);
struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
- spin_lock(&pp->lock);
- /* Configuring the driver for a new CPU while the
- * driver is stopping is racy, so just avoid it.
- */
- if (pp->is_stopped) {
- spin_unlock(&pp->lock);
- break;
- }
- netif_tx_stop_all_queues(pp->dev);
- /* We have to synchronise on tha napi of each CPU
- * except the one just being waked up
- */
- for_each_online_cpu(other_cpu) {
- if (other_cpu != cpu) {
- struct mvneta_pcpu_port *other_port =
- per_cpu_ptr(pp->ports, other_cpu);
+ spin_lock(&pp->lock);
+ /*
+ * Configuring the driver for a new CPU while the driver is
+ * stopping is racy, so just avoid it.
+ */
+ if (pp->is_stopped) {
+ spin_unlock(&pp->lock);
+ return 0;
+ }
+ netif_tx_stop_all_queues(pp->dev);
- napi_synchronize(&other_port->napi);
- }
+ /*
+ * We have to synchronise on tha napi of each CPU except the one
+ * just being woken up
+ */
+ for_each_online_cpu(other_cpu) {
+ if (other_cpu != cpu) {
+ struct mvneta_pcpu_port *other_port =
+ per_cpu_ptr(pp->ports, other_cpu);
+
+ napi_synchronize(&other_port->napi);
}
+ }
- /* Mask all ethernet port interrupts */
- on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
- napi_enable(&port->napi);
+ /* Mask all ethernet port interrupts */
+ on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
+ napi_enable(&port->napi);
+ /*
+ * Enable per-CPU interrupts on the CPU that is
+ * brought up.
+ */
+ mvneta_percpu_enable(pp);
- /* Enable per-CPU interrupts on the CPU that is
- * brought up.
- */
- mvneta_percpu_enable(pp);
+ /*
+ * Enable per-CPU interrupt on the one CPU we care
+ * about.
+ */
+ mvneta_percpu_elect(pp);
- /* Enable per-CPU interrupt on the one CPU we care
- * about.
- */
- mvneta_percpu_elect(pp);
-
- /* Unmask all ethernet port interrupts */
- on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
- mvreg_write(pp, MVNETA_INTR_MISC_MASK,
- MVNETA_CAUSE_PHY_STATUS_CHANGE |
- MVNETA_CAUSE_LINK_CHANGE |
- MVNETA_CAUSE_PSC_SYNC_CHANGE);
- netif_tx_start_all_queues(pp->dev);
- spin_unlock(&pp->lock);
- break;
- case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
- netif_tx_stop_all_queues(pp->dev);
- /* Thanks to this lock we are sure that any pending
- * cpu election is done
- */
- spin_lock(&pp->lock);
- /* Mask all ethernet port interrupts */
- on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
- spin_unlock(&pp->lock);
+ /* Unmask all ethernet port interrupts */
+ on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+ MVNETA_CAUSE_PHY_STATUS_CHANGE |
+ MVNETA_CAUSE_LINK_CHANGE |
+ MVNETA_CAUSE_PSC_SYNC_CHANGE);
+ netif_tx_start_all_queues(pp->dev);
+ spin_unlock(&pp->lock);
+ return 0;
+}
- napi_synchronize(&port->napi);
- napi_disable(&port->napi);
- /* Disable per-CPU interrupts on the CPU that is
- * brought down.
- */
- mvneta_percpu_disable(pp);
+static int mvneta_cpu_down_prepare(unsigned int cpu, struct hlist_node *node)
+{
+ struct mvneta_port *pp = hlist_entry_safe(node, struct mvneta_port,
+ node_online);
+ struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- /* Check if a new CPU must be elected now this on is down */
- spin_lock(&pp->lock);
- mvneta_percpu_elect(pp);
- spin_unlock(&pp->lock);
- /* Unmask all ethernet port interrupts */
- on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
- mvreg_write(pp, MVNETA_INTR_MISC_MASK,
- MVNETA_CAUSE_PHY_STATUS_CHANGE |
- MVNETA_CAUSE_LINK_CHANGE |
- MVNETA_CAUSE_PSC_SYNC_CHANGE);
- netif_tx_start_all_queues(pp->dev);
- break;
- }
+ /*
+ * Thanks to this lock we are sure that any pending cpu election is
+ * done.
+ */
+ spin_lock(&pp->lock);
+ /* Mask all ethernet port interrupts */
+ on_each_cpu(mvneta_percpu_mask_interrupt, pp, true);
+ spin_unlock(&pp->lock);
- return NOTIFY_OK;
+ napi_synchronize(&port->napi);
+ napi_disable(&port->napi);
+ /* Disable per-CPU interrupts on the CPU that is brought down. */
+ mvneta_percpu_disable(pp);
+ return 0;
+}
+
+static int mvneta_cpu_dead(unsigned int cpu, struct hlist_node *node)
+{
+ struct mvneta_port *pp = hlist_entry_safe(node, struct mvneta_port,
+ node_dead);
+
+ /* Check if a new CPU must be elected now this on is down */
+ spin_lock(&pp->lock);
+ mvneta_percpu_elect(pp);
+ spin_unlock(&pp->lock);
+ /* Unmask all ethernet port interrupts */
+ on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
+ mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+ MVNETA_CAUSE_PHY_STATUS_CHANGE |
+ MVNETA_CAUSE_LINK_CHANGE |
+ MVNETA_CAUSE_PSC_SYNC_CHANGE);
+ netif_tx_start_all_queues(pp->dev);
+ return 0;
}
static int mvneta_open(struct net_device *dev)
@@ -3442,7 +3447,15 @@ static int mvneta_open(struct net_device *dev)
/* Register a CPU notifier to handle the case where our CPU
* might be taken offline.
*/
- register_cpu_notifier(&pp->cpu_notifier);
+ ret = cpuhp_state_add_instance_nocalls(online_hpstate,
+ &pp->node_online);
+ if (ret)
+ goto err_free_irq;
+
+ ret = cpuhp_state_add_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
+ &pp->node_dead);
+ if (ret)
+ goto err_free_online_hp;
/* In default link is down */
netif_carrier_off(pp->dev);
@@ -3450,15 +3463,19 @@ static int mvneta_open(struct net_device *dev)
ret = mvneta_mdio_probe(pp);
if (ret < 0) {
netdev_err(dev, "cannot probe MDIO bus\n");
- goto err_free_irq;
+ goto err_free_dead_hp;
}
mvneta_start_dev(pp);
return 0;
+err_free_dead_hp:
+ cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
+ &pp->node_dead);
+err_free_online_hp:
+ cpuhp_state_remove_instance_nocalls(online_hpstate, &pp->node_online);
err_free_irq:
- unregister_cpu_notifier(&pp->cpu_notifier);
on_each_cpu(mvneta_percpu_disable, pp, true);
free_percpu_irq(pp->dev->irq, pp->ports);
err_cleanup_txqs:
@@ -3484,7 +3501,10 @@ static int mvneta_stop(struct net_device *dev)
mvneta_stop_dev(pp);
mvneta_mdio_remove(pp);
- unregister_cpu_notifier(&pp->cpu_notifier);
+
+ cpuhp_state_remove_instance_nocalls(online_hpstate, &pp->node_online);
+ cpuhp_state_remove_instance_nocalls(CPUHP_NET_MVNETA_DEAD,
+ &pp->node_dead);
on_each_cpu(mvneta_percpu_disable, pp, true);
free_percpu_irq(dev->irq, pp->ports);
mvneta_cleanup_rxqs(pp);
@@ -4024,7 +4044,6 @@ static int mvneta_probe(struct platform_device *pdev)
err = of_property_read_string(dn, "managed", &managed);
pp->use_inband_status = (err == 0 &&
strcmp(managed, "in-band-status") == 0);
- pp->cpu_notifier.notifier_call = mvneta_percpu_notifier;
pp->rxq_def = rxq_def;
@@ -4227,7 +4246,42 @@ static struct platform_driver mvneta_driver = {
},
};
-module_platform_driver(mvneta_driver);
+static int __init mvneta_driver_init(void)
+{
+ int ret;
+
+ ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "net/mvmeta:online",
+ mvneta_cpu_online,
+ mvneta_cpu_down_prepare);
+ if (ret < 0)
+ goto out;
+ online_hpstate = ret;
+ ret = cpuhp_setup_state_multi(CPUHP_NET_MVNETA_DEAD, "net/mvneta:dead",
+ NULL, mvneta_cpu_dead);
+ if (ret)
+ goto err_dead;
+
+ ret = platform_driver_register(&mvneta_driver);
+ if (ret)
+ goto err;
+ return 0;
+
+err:
+ cpuhp_remove_multi_state(CPUHP_NET_MVNETA_DEAD);
+err_dead:
+ cpuhp_remove_multi_state(online_hpstate);
+out:
+ return ret;
+}
+module_init(mvneta_driver_init);
+
+static void __exit mvneta_driver_exit(void)
+{
+ platform_driver_unregister(&mvneta_driver);
+ cpuhp_remove_multi_state(CPUHP_NET_MVNETA_DEAD);
+ cpuhp_remove_multi_state(online_hpstate);
+}
+module_exit(mvneta_driver_exit);
MODULE_DESCRIPTION("Marvell NETA Ethernet Driver - www.marvell.com");
MODULE_AUTHOR("Rami Rosen <rosenr@marvell.com>, Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 1b5f531..fad84f3 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -138,8 +138,9 @@ struct virtnet_info {
/* Does the affinity hint is set for virtqueues? */
bool affinity_hint_set;
- /* CPU hot plug notifier */
- struct notifier_block nb;
+ /* CPU hotplug instances for online & dead */
+ struct hlist_node node;
+ struct hlist_node node_dead;
/* Control VQ buffers: protected by the rtnl lock */
struct virtio_net_ctrl_hdr ctrl_hdr;
@@ -1237,25 +1238,53 @@ static void virtnet_set_affinity(struct virtnet_info *vi)
vi->affinity_hint_set = true;
}
-static int virtnet_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
+static int virtnet_cpu_online(unsigned int cpu, struct hlist_node *node)
{
- struct virtnet_info *vi = container_of(nfb, struct virtnet_info, nb);
+ struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info,
+ node);
+ virtnet_set_affinity(vi);
+ return 0;
+}
- switch(action & ~CPU_TASKS_FROZEN) {
- case CPU_ONLINE:
- case CPU_DOWN_FAILED:
- case CPU_DEAD:
- virtnet_set_affinity(vi);
- break;
- case CPU_DOWN_PREPARE:
- virtnet_clean_affinity(vi, (long)hcpu);
- break;
- default:
- break;
- }
+static int virtnet_cpu_dead(unsigned int cpu, struct hlist_node *node)
+{
+ struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info,
+ node_dead);
+ virtnet_set_affinity(vi);
+ return 0;
+}
- return NOTIFY_OK;
+static int virtnet_cpu_down_prep(unsigned int cpu, struct hlist_node *node)
+{
+ struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info,
+ node);
+
+ virtnet_clean_affinity(vi, cpu);
+ return 0;
+}
+
+static enum cpuhp_state virtionet_online;
+
+static int virtnet_cpu_notif_add(struct virtnet_info *vi)
+{
+ int ret;
+
+ ret = cpuhp_state_add_instance_nocalls(virtionet_online, &vi->node);
+ if (ret)
+ return ret;
+ ret = cpuhp_state_add_instance_nocalls(CPUHP_VIRT_NET_DEAD,
+ &vi->node_dead);
+ if (!ret)
+ return ret;
+ cpuhp_state_remove_instance_nocalls(virtionet_online, &vi->node);
+ return ret;
+}
+
+static void virtnet_cpu_notif_remove(struct virtnet_info *vi)
+{
+ cpuhp_state_remove_instance_nocalls(virtionet_online, &vi->node);
+ cpuhp_state_remove_instance_nocalls(CPUHP_VIRT_NET_DEAD,
+ &vi->node_dead);
}
static void virtnet_get_ringparam(struct net_device *dev,
@@ -1879,8 +1908,7 @@ static int virtnet_probe(struct virtio_device *vdev)
virtio_device_ready(vdev);
- vi->nb.notifier_call = &virtnet_cpu_callback;
- err = register_hotcpu_notifier(&vi->nb);
+ err = virtnet_cpu_notif_add(vi);
if (err) {
pr_debug("virtio_net: registering cpu notifier failed\n");
goto free_unregister_netdev;
@@ -1934,7 +1962,7 @@ static void virtnet_remove(struct virtio_device *vdev)
{
struct virtnet_info *vi = vdev->priv;
- unregister_hotcpu_notifier(&vi->nb);
+ virtnet_cpu_notif_remove(vi);
/* Make sure no work handler is accessing the device. */
flush_work(&vi->config_work);
@@ -1953,7 +1981,7 @@ static int virtnet_freeze(struct virtio_device *vdev)
struct virtnet_info *vi = vdev->priv;
int i;
- unregister_hotcpu_notifier(&vi->nb);
+ virtnet_cpu_notif_remove(vi);
/* Make sure no work handler is accessing the device */
flush_work(&vi->config_work);
@@ -1997,7 +2025,7 @@ static int virtnet_restore(struct virtio_device *vdev)
virtnet_set_queues(vi, vi->curr_queue_pairs);
rtnl_unlock();
- err = register_hotcpu_notifier(&vi->nb);
+ err = virtnet_cpu_notif_add(vi);
if (err)
return err;
@@ -2039,7 +2067,41 @@ static struct virtio_driver virtio_net_driver = {
#endif
};
-module_virtio_driver(virtio_net_driver);
+static __init int virtio_net_driver_init(void)
+{
+ int ret;
+
+ ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "AP_VIRT_NET_ONLINE",
+ virtnet_cpu_online,
+ virtnet_cpu_down_prep);
+ if (ret < 0)
+ goto out;
+ virtionet_online = ret;
+ ret = cpuhp_setup_state_multi(CPUHP_VIRT_NET_DEAD, "VIRT_NET_DEAD",
+ NULL, virtnet_cpu_dead);
+ if (ret)
+ goto err_dead;
+
+ ret = register_virtio_driver(&virtio_net_driver);
+ if (ret)
+ goto err_virtio;
+ return 0;
+err_virtio:
+ cpuhp_remove_multi_state(CPUHP_VIRT_NET_DEAD);
+err_dead:
+ cpuhp_remove_multi_state(virtionet_online);
+out:
+ return ret;
+}
+module_init(virtio_net_driver_init);
+
+static __exit void virtio_net_driver_exit(void)
+{
+ cpuhp_remove_multi_state(CPUHP_VIRT_NET_DEAD);
+ cpuhp_remove_multi_state(virtionet_online);
+ unregister_virtio_driver(&virtio_net_driver);
+}
+module_exit(virtio_net_driver_exit);
MODULE_DEVICE_TABLE(virtio, id_table);
MODULE_DESCRIPTION("Virtio network driver");
diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
index 6808db4..ec3a64e 100644
--- a/drivers/net/wireless/ath/carl9170/debug.c
+++ b/drivers/net/wireless/ath/carl9170/debug.c
@@ -75,7 +75,8 @@ static ssize_t carl9170_debugfs_read(struct file *file, char __user *userbuf,
if (!ar)
return -ENODEV;
- dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops);
+ dfops = container_of(debugfs_real_fops(file),
+ struct carl9170_debugfs_fops, fops);
if (!dfops->read)
return -ENOSYS;
@@ -127,7 +128,8 @@ static ssize_t carl9170_debugfs_write(struct file *file,
if (!ar)
return -ENODEV;
- dfops = container_of(file->f_op, struct carl9170_debugfs_fops, fops);
+ dfops = container_of(debugfs_real_fops(file),
+ struct carl9170_debugfs_fops, fops);
if (!dfops->write)
return -ENOSYS;
diff --git a/drivers/net/wireless/broadcom/b43/debugfs.c b/drivers/net/wireless/broadcom/b43/debugfs.c
index b4bcd94..7704638 100644
--- a/drivers/net/wireless/broadcom/b43/debugfs.c
+++ b/drivers/net/wireless/broadcom/b43/debugfs.c
@@ -524,7 +524,8 @@ static ssize_t b43_debugfs_read(struct file *file, char __user *userbuf,
goto out_unlock;
}
- dfops = container_of(file->f_op, struct b43_debugfs_fops, fops);
+ dfops = container_of(debugfs_real_fops(file),
+ struct b43_debugfs_fops, fops);
if (!dfops->read) {
err = -ENOSYS;
goto out_unlock;
@@ -585,7 +586,8 @@ static ssize_t b43_debugfs_write(struct file *file,
goto out_unlock;
}
- dfops = container_of(file->f_op, struct b43_debugfs_fops, fops);
+ dfops = container_of(debugfs_real_fops(file),
+ struct b43_debugfs_fops, fops);
if (!dfops->write) {
err = -ENOSYS;
goto out_unlock;
diff --git a/drivers/net/wireless/broadcom/b43legacy/debugfs.c b/drivers/net/wireless/broadcom/b43legacy/debugfs.c
index 090910e..82ef56e 100644
--- a/drivers/net/wireless/broadcom/b43legacy/debugfs.c
+++ b/drivers/net/wireless/broadcom/b43legacy/debugfs.c
@@ -221,7 +221,8 @@ static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
goto out_unlock;
}
- dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops);
+ dfops = container_of(debugfs_real_fops(file),
+ struct b43legacy_debugfs_fops, fops);
if (!dfops->read) {
err = -ENOSYS;
goto out_unlock;
@@ -287,7 +288,8 @@ static ssize_t b43legacy_debugfs_write(struct file *file,
goto out_unlock;
}
- dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops);
+ dfops = container_of(debugfs_real_fops(file),
+ struct b43legacy_debugfs_fops, fops);
if (!dfops->write) {
err = -ENOSYS;
goto out_unlock;
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c
index 715583f..4d7bbd2 100644
--- a/drivers/nvdimm/core.c
+++ b/drivers/nvdimm/core.c
@@ -99,8 +99,11 @@ static struct nvdimm_map *alloc_nvdimm_map(struct device *dev,
nvdimm_map->size = size;
kref_init(&nvdimm_map->kref);
- if (!request_mem_region(offset, size, dev_name(&nvdimm_bus->dev)))
+ if (!request_mem_region(offset, size, dev_name(&nvdimm_bus->dev))) {
+ dev_err(&nvdimm_bus->dev, "failed to request %pa + %zd for %s\n",
+ &offset, size, dev_name(dev));
goto err_request_region;
+ }
if (flags)
nvdimm_map->mem = memremap(offset, size, flags);
@@ -171,6 +174,9 @@ void *devm_nvdimm_memremap(struct device *dev, resource_size_t offset,
kref_get(&nvdimm_map->kref);
nvdimm_bus_unlock(dev);
+ if (!nvdimm_map)
+ return NULL;
+
if (devm_add_action_or_reset(dev, nvdimm_map_put, nvdimm_map))
return NULL;
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 8024a0e..0b78a82 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -52,10 +52,28 @@ struct nvdimm_drvdata {
struct nd_region_data {
int ns_count;
int ns_active;
- unsigned int flush_mask;
- void __iomem *flush_wpq[0][0];
+ unsigned int hints_shift;
+ void __iomem *flush_wpq[0];
};
+static inline void __iomem *ndrd_get_flush_wpq(struct nd_region_data *ndrd,
+ int dimm, int hint)
+{
+ unsigned int num = 1 << ndrd->hints_shift;
+ unsigned int mask = num - 1;
+
+ return ndrd->flush_wpq[dimm * num + (hint & mask)];
+}
+
+static inline void ndrd_set_flush_wpq(struct nd_region_data *ndrd, int dimm,
+ int hint, void __iomem *flush)
+{
+ unsigned int num = 1 << ndrd->hints_shift;
+ unsigned int mask = num - 1;
+
+ ndrd->flush_wpq[dimm * num + (hint & mask)] = flush;
+}
+
static inline struct nd_namespace_index *to_namespace_index(
struct nvdimm_drvdata *ndd, int i)
{
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index e8d5ba7..4c0ac4a 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -38,7 +38,7 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,
dev_dbg(dev, "%s: map %d flush address%s\n", nvdimm_name(nvdimm),
nvdimm->num_flush, nvdimm->num_flush == 1 ? "" : "es");
- for (i = 0; i < nvdimm->num_flush; i++) {
+ for (i = 0; i < (1 << ndrd->hints_shift); i++) {
struct resource *res = &nvdimm->flush_wpq[i];
unsigned long pfn = PHYS_PFN(res->start);
void __iomem *flush_page;
@@ -54,14 +54,15 @@ static int nvdimm_map_flush(struct device *dev, struct nvdimm *nvdimm, int dimm,
if (j < i)
flush_page = (void __iomem *) ((unsigned long)
- ndrd->flush_wpq[dimm][j] & PAGE_MASK);
+ ndrd_get_flush_wpq(ndrd, dimm, j)
+ & PAGE_MASK);
else
flush_page = devm_nvdimm_ioremap(dev,
- PHYS_PFN(pfn), PAGE_SIZE);
+ PFN_PHYS(pfn), PAGE_SIZE);
if (!flush_page)
return -ENXIO;
- ndrd->flush_wpq[dimm][i] = flush_page
- + (res->start & ~PAGE_MASK);
+ ndrd_set_flush_wpq(ndrd, dimm, i, flush_page
+ + (res->start & ~PAGE_MASK));
}
return 0;
@@ -93,7 +94,10 @@ int nd_region_activate(struct nd_region *nd_region)
return -ENOMEM;
dev_set_drvdata(dev, ndrd);
- ndrd->flush_mask = (1 << ilog2(num_flush)) - 1;
+ if (!num_flush)
+ return 0;
+
+ ndrd->hints_shift = ilog2(num_flush);
for (i = 0; i < nd_region->ndr_mappings; i++) {
struct nd_mapping *nd_mapping = &nd_region->mapping[i];
struct nvdimm *nvdimm = nd_mapping->nvdimm;
@@ -900,8 +904,8 @@ void nvdimm_flush(struct nd_region *nd_region)
*/
wmb();
for (i = 0; i < nd_region->ndr_mappings; i++)
- if (ndrd->flush_wpq[i][0])
- writeq(1, ndrd->flush_wpq[i][idx & ndrd->flush_mask]);
+ if (ndrd_get_flush_wpq(ndrd, i, 0))
+ writeq(1, ndrd_get_flush_wpq(ndrd, i, idx));
wmb();
}
EXPORT_SYMBOL_GPL(nvdimm_flush);
@@ -925,7 +929,7 @@ int nvdimm_has_flush(struct nd_region *nd_region)
for (i = 0; i < nd_region->ndr_mappings; i++)
/* flush hints present, flushing required */
- if (ndrd->flush_wpq[i][0])
+ if (ndrd_get_flush_wpq(ndrd, i, 0))
return 1;
/*
diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c
index 4d3f391..423907b 100644
--- a/drivers/nvmem/rockchip-efuse.c
+++ b/drivers/nvmem/rockchip-efuse.c
@@ -22,17 +22,29 @@
#include <linux/nvmem-provider.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/platform_device.h>
-#define EFUSE_A_SHIFT 6
-#define EFUSE_A_MASK 0x3ff
-#define EFUSE_PGENB BIT(3)
-#define EFUSE_LOAD BIT(2)
-#define EFUSE_STROBE BIT(1)
-#define EFUSE_CSB BIT(0)
-
-#define REG_EFUSE_CTRL 0x0000
-#define REG_EFUSE_DOUT 0x0004
+#define RK3288_A_SHIFT 6
+#define RK3288_A_MASK 0x3ff
+#define RK3288_PGENB BIT(3)
+#define RK3288_LOAD BIT(2)
+#define RK3288_STROBE BIT(1)
+#define RK3288_CSB BIT(0)
+
+#define RK3399_A_SHIFT 16
+#define RK3399_A_MASK 0x3ff
+#define RK3399_NBYTES 4
+#define RK3399_STROBSFTSEL BIT(9)
+#define RK3399_RSB BIT(7)
+#define RK3399_PD BIT(5)
+#define RK3399_PGENB BIT(3)
+#define RK3399_LOAD BIT(2)
+#define RK3399_STROBE BIT(1)
+#define RK3399_CSB BIT(0)
+
+#define REG_EFUSE_CTRL 0x0000
+#define REG_EFUSE_DOUT 0x0004
struct rockchip_efuse_chip {
struct device *dev;
@@ -40,8 +52,8 @@ struct rockchip_efuse_chip {
struct clk *clk;
};
-static int rockchip_efuse_read(void *context, unsigned int offset,
- void *val, size_t bytes)
+static int rockchip_rk3288_efuse_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
{
struct rockchip_efuse_chip *efuse = context;
u8 *buf = val;
@@ -53,27 +65,82 @@ static int rockchip_efuse_read(void *context, unsigned int offset,
return ret;
}
- writel(EFUSE_LOAD | EFUSE_PGENB, efuse->base + REG_EFUSE_CTRL);
+ writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL);
udelay(1);
while (bytes--) {
writel(readl(efuse->base + REG_EFUSE_CTRL) &
- (~(EFUSE_A_MASK << EFUSE_A_SHIFT)),
+ (~(RK3288_A_MASK << RK3288_A_SHIFT)),
efuse->base + REG_EFUSE_CTRL);
writel(readl(efuse->base + REG_EFUSE_CTRL) |
- ((offset++ & EFUSE_A_MASK) << EFUSE_A_SHIFT),
+ ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT),
efuse->base + REG_EFUSE_CTRL);
udelay(1);
writel(readl(efuse->base + REG_EFUSE_CTRL) |
- EFUSE_STROBE, efuse->base + REG_EFUSE_CTRL);
+ RK3288_STROBE, efuse->base + REG_EFUSE_CTRL);
udelay(1);
*buf++ = readb(efuse->base + REG_EFUSE_DOUT);
writel(readl(efuse->base + REG_EFUSE_CTRL) &
- (~EFUSE_STROBE), efuse->base + REG_EFUSE_CTRL);
+ (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL);
+ udelay(1);
+ }
+
+ /* Switch to standby mode */
+ writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL);
+
+ clk_disable_unprepare(efuse->clk);
+
+ return 0;
+}
+
+static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,
+ void *val, size_t bytes)
+{
+ struct rockchip_efuse_chip *efuse = context;
+ unsigned int addr_start, addr_end, addr_offset, addr_len;
+ u32 out_value;
+ u8 *buf;
+ int ret, i = 0;
+
+ ret = clk_prepare_enable(efuse->clk);
+ if (ret < 0) {
+ dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
+ return ret;
+ }
+
+ addr_start = rounddown(offset, RK3399_NBYTES) / RK3399_NBYTES;
+ addr_end = roundup(offset + bytes, RK3399_NBYTES) / RK3399_NBYTES;
+ addr_offset = offset % RK3399_NBYTES;
+ addr_len = addr_end - addr_start;
+
+ buf = kzalloc(sizeof(*buf) * addr_len * RK3399_NBYTES, GFP_KERNEL);
+ if (!buf) {
+ clk_disable_unprepare(efuse->clk);
+ return -ENOMEM;
+ }
+
+ writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
+ efuse->base + REG_EFUSE_CTRL);
+ udelay(1);
+ while (addr_len--) {
+ writel(readl(efuse->base + REG_EFUSE_CTRL) | RK3399_STROBE |
+ ((addr_start++ & RK3399_A_MASK) << RK3399_A_SHIFT),
+ efuse->base + REG_EFUSE_CTRL);
udelay(1);
+ out_value = readl(efuse->base + REG_EFUSE_DOUT);
+ writel(readl(efuse->base + REG_EFUSE_CTRL) & (~RK3399_STROBE),
+ efuse->base + REG_EFUSE_CTRL);
+ udelay(1);
+
+ memcpy(&buf[i], &out_value, RK3399_NBYTES);
+ i += RK3399_NBYTES;
}
/* Switch to standby mode */
- writel(EFUSE_PGENB | EFUSE_CSB, efuse->base + REG_EFUSE_CTRL);
+ writel(RK3399_PD | RK3399_CSB, efuse->base + REG_EFUSE_CTRL);
+
+ memcpy(val, buf + addr_offset, bytes);
+
+ kfree(buf);
clk_disable_unprepare(efuse->clk);
@@ -89,7 +156,27 @@ static struct nvmem_config econfig = {
};
static const struct of_device_id rockchip_efuse_match[] = {
- { .compatible = "rockchip,rockchip-efuse", },
+ /* deprecated but kept around for dts binding compatibility */
+ {
+ .compatible = "rockchip,rockchip-efuse",
+ .data = (void *)&rockchip_rk3288_efuse_read,
+ },
+ {
+ .compatible = "rockchip,rk3066a-efuse",
+ .data = (void *)&rockchip_rk3288_efuse_read,
+ },
+ {
+ .compatible = "rockchip,rk3188-efuse",
+ .data = (void *)&rockchip_rk3288_efuse_read,
+ },
+ {
+ .compatible = "rockchip,rk3288-efuse",
+ .data = (void *)&rockchip_rk3288_efuse_read,
+ },
+ {
+ .compatible = "rockchip,rk3399-efuse",
+ .data = (void *)&rockchip_rk3399_efuse_read,
+ },
{ /* sentinel */},
};
MODULE_DEVICE_TABLE(of, rockchip_efuse_match);
@@ -99,6 +186,14 @@ static int rockchip_efuse_probe(struct platform_device *pdev)
struct resource *res;
struct nvmem_device *nvmem;
struct rockchip_efuse_chip *efuse;
+ const struct of_device_id *match;
+ struct device *dev = &pdev->dev;
+
+ match = of_match_device(dev->driver->of_match_table, dev);
+ if (!match || !match->data) {
+ dev_err(dev, "failed to get match data\n");
+ return -EINVAL;
+ }
efuse = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_efuse_chip),
GFP_KERNEL);
@@ -116,7 +211,7 @@ static int rockchip_efuse_probe(struct platform_device *pdev)
efuse->dev = &pdev->dev;
econfig.size = resource_size(res);
- econfig.reg_read = rockchip_efuse_read;
+ econfig.reg_read = match->data;
econfig.priv = efuse;
econfig.dev = efuse->dev;
nvmem = nvmem_register(&econfig);
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 085c638..c89d5d2 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -924,7 +924,7 @@ static inline void early_init_dt_check_for_initrd(unsigned long node)
#ifdef CONFIG_SERIAL_EARLYCON
-static int __init early_init_dt_scan_chosen_serial(void)
+int __init early_init_dt_scan_chosen_stdout(void)
{
int offset;
const char *p, *q, *options = NULL;
@@ -968,15 +968,6 @@ static int __init early_init_dt_scan_chosen_serial(void)
}
return -ENODEV;
}
-
-static int __init setup_of_earlycon(char *buf)
-{
- if (buf)
- return 0;
-
- return early_init_dt_scan_chosen_serial();
-}
-early_param("earlycon", setup_of_earlycon);
#endif
/**
diff --git a/drivers/of/of_numa.c b/drivers/of/of_numa.c
index ed5a097..f63d4b0d 100644
--- a/drivers/of/of_numa.c
+++ b/drivers/of/of_numa.c
@@ -16,6 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#define pr_fmt(fmt) "OF: NUMA: " fmt
+
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/nodemask.h>
@@ -49,10 +51,9 @@ static void __init of_numa_parse_cpu_nodes(void)
if (r)
continue;
- pr_debug("NUMA: CPU on %u\n", nid);
+ pr_debug("CPU on %u\n", nid);
if (nid >= MAX_NUMNODES)
- pr_warn("NUMA: Node id %u exceeds maximum value\n",
- nid);
+ pr_warn("Node id %u exceeds maximum value\n", nid);
else
node_set(nid, numa_nodes_parsed);
}
@@ -63,13 +64,9 @@ static int __init of_numa_parse_memory_nodes(void)
struct device_node *np = NULL;
struct resource rsrc;
u32 nid;
- int r = 0;
-
- for (;;) {
- np = of_find_node_by_type(np, "memory");
- if (!np)
- break;
+ int i, r;
+ for_each_node_by_type(np, "memory") {
r = of_property_read_u32(np, "numa-node-id", &nid);
if (r == -EINVAL)
/*
@@ -78,27 +75,23 @@ static int __init of_numa_parse_memory_nodes(void)
* "numa-node-id" property
*/
continue;
- else if (r)
- /* some other error */
- break;
- r = of_address_to_resource(np, 0, &rsrc);
- if (r) {
- pr_err("NUMA: bad reg property in memory node\n");
- break;
+ if (nid >= MAX_NUMNODES) {
+ pr_warn("Node id %u exceeds maximum value\n", nid);
+ r = -EINVAL;
}
- pr_debug("NUMA: base = %llx len = %llx, node = %u\n",
- rsrc.start, rsrc.end - rsrc.start + 1, nid);
-
+ for (i = 0; !r && !of_address_to_resource(np, i, &rsrc); i++)
+ r = numa_add_memblk(nid, rsrc.start, rsrc.end + 1);
- r = numa_add_memblk(nid, rsrc.start, rsrc.end + 1);
- if (r)
- break;
+ if (!i || r) {
+ of_node_put(np);
+ pr_err("bad property in memory node\n");
+ return r ? : -EINVAL;
+ }
}
- of_node_put(np);
- return r;
+ return 0;
}
static int __init of_numa_parse_distance_map_v1(struct device_node *map)
@@ -107,17 +100,17 @@ static int __init of_numa_parse_distance_map_v1(struct device_node *map)
int entry_count;
int i;
- pr_info("NUMA: parsing numa-distance-map-v1\n");
+ pr_info("parsing numa-distance-map-v1\n");
matrix = of_get_property(map, "distance-matrix", NULL);
if (!matrix) {
- pr_err("NUMA: No distance-matrix property in distance-map\n");
+ pr_err("No distance-matrix property in distance-map\n");
return -EINVAL;
}
entry_count = of_property_count_u32_elems(map, "distance-matrix");
if (entry_count <= 0) {
- pr_err("NUMA: Invalid distance-matrix\n");
+ pr_err("Invalid distance-matrix\n");
return -EINVAL;
}
@@ -132,7 +125,7 @@ static int __init of_numa_parse_distance_map_v1(struct device_node *map)
matrix++;
numa_set_distance(nodea, nodeb, distance);
- pr_debug("NUMA: distance[node%d -> node%d] = %d\n",
+ pr_debug("distance[node%d -> node%d] = %d\n",
nodea, nodeb, distance);
/* Set default distance of node B->A same as A->B */
@@ -166,8 +159,6 @@ int of_node_to_nid(struct device_node *device)
np = of_node_get(device);
while (np) {
- struct device_node *parent;
-
r = of_property_read_u32(np, "numa-node-id", &nid);
/*
* -EINVAL indicates the property was not found, and
@@ -178,22 +169,15 @@ int of_node_to_nid(struct device_node *device)
if (r != -EINVAL)
break;
- parent = of_get_parent(np);
- of_node_put(np);
- np = parent;
+ np = of_get_next_parent(np);
}
if (np && r)
- pr_warn("NUMA: Invalid \"numa-node-id\" property in node %s\n",
+ pr_warn("Invalid \"numa-node-id\" property in node %s\n",
np->name);
of_node_put(np);
- if (!r) {
- if (nid >= MAX_NUMNODES)
- pr_warn("NUMA: Node id %u exceeds maximum value\n",
- nid);
- else
- return nid;
- }
+ if (!r)
+ return nid;
return NUMA_NO_NODE;
}
diff --git a/drivers/oprofile/timer_int.c b/drivers/oprofile/timer_int.c
index bdef916..2498a6c 100644
--- a/drivers/oprofile/timer_int.c
+++ b/drivers/oprofile/timer_int.c
@@ -74,37 +74,39 @@ static void oprofile_hrtimer_stop(void)
put_online_cpus();
}
-static int oprofile_cpu_notify(struct notifier_block *self,
- unsigned long action, void *hcpu)
+static int oprofile_timer_online(unsigned int cpu)
{
- long cpu = (long) hcpu;
-
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- smp_call_function_single(cpu, __oprofile_hrtimer_start,
- NULL, 1);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- __oprofile_hrtimer_stop(cpu);
- break;
- }
- return NOTIFY_OK;
+ local_irq_disable();
+ __oprofile_hrtimer_start(NULL);
+ local_irq_enable();
+ return 0;
}
-static struct notifier_block __refdata oprofile_cpu_notifier = {
- .notifier_call = oprofile_cpu_notify,
-};
+static int oprofile_timer_prep_down(unsigned int cpu)
+{
+ __oprofile_hrtimer_stop(cpu);
+ return 0;
+}
+
+static enum cpuhp_state hp_online;
static int oprofile_hrtimer_setup(void)
{
- return register_hotcpu_notifier(&oprofile_cpu_notifier);
+ int ret;
+
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "oprofile/timer:online",
+ oprofile_timer_online,
+ oprofile_timer_prep_down);
+ if (ret < 0)
+ return ret;
+ hp_online = ret;
+ return 0;
}
static void oprofile_hrtimer_shutdown(void)
{
- unregister_hotcpu_notifier(&oprofile_cpu_notifier);
+ cpuhp_remove_state_nocalls(hp_online);
}
int oprofile_timer_init(struct oprofile_operations *ops)
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 98f1222..bfdd074 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -19,6 +19,7 @@
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/io.h>
+#include <linux/acpi_iort.h>
#include <linux/slab.h>
#include <linux/irqdomain.h>
#include <linux/of_irq.h>
@@ -549,15 +550,23 @@ error_attrs:
return ret;
}
-static struct msi_desc *msi_setup_entry(struct pci_dev *dev, int nvec)
+static struct msi_desc *
+msi_setup_entry(struct pci_dev *dev, int nvec, bool affinity)
{
- u16 control;
+ struct cpumask *masks = NULL;
struct msi_desc *entry;
+ u16 control;
+
+ if (affinity) {
+ masks = irq_create_affinity_masks(dev->irq_affinity, nvec);
+ if (!masks)
+ pr_err("Unable to allocate affinity masks, ignoring\n");
+ }
/* MSI Entry Initialization */
- entry = alloc_msi_entry(&dev->dev);
+ entry = alloc_msi_entry(&dev->dev, nvec, masks);
if (!entry)
- return NULL;
+ goto out;
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
@@ -568,8 +577,6 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev, int nvec)
entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */
entry->msi_attrib.multi_cap = (control & PCI_MSI_FLAGS_QMASK) >> 1;
entry->msi_attrib.multiple = ilog2(__roundup_pow_of_two(nvec));
- entry->nvec_used = nvec;
- entry->affinity = dev->irq_affinity;
if (control & PCI_MSI_FLAGS_64BIT)
entry->mask_pos = dev->msi_cap + PCI_MSI_MASK_64;
@@ -580,6 +587,8 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev, int nvec)
if (entry->msi_attrib.maskbit)
pci_read_config_dword(dev, entry->mask_pos, &entry->masked);
+out:
+ kfree(masks);
return entry;
}
@@ -608,7 +617,7 @@ static int msi_verify_entries(struct pci_dev *dev)
* an error, and a positive return value indicates the number of interrupts
* which could have been allocated.
*/
-static int msi_capability_init(struct pci_dev *dev, int nvec)
+static int msi_capability_init(struct pci_dev *dev, int nvec, bool affinity)
{
struct msi_desc *entry;
int ret;
@@ -616,7 +625,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
pci_msi_set_enable(dev, 0); /* Disable MSI during set up */
- entry = msi_setup_entry(dev, nvec);
+ entry = msi_setup_entry(dev, nvec, affinity);
if (!entry)
return -ENOMEM;
@@ -679,28 +688,29 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries)
}
static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,
- struct msix_entry *entries, int nvec)
+ struct msix_entry *entries, int nvec,
+ bool affinity)
{
- const struct cpumask *mask = NULL;
+ struct cpumask *curmsk, *masks = NULL;
struct msi_desc *entry;
- int cpu = -1, i;
-
- for (i = 0; i < nvec; i++) {
- if (dev->irq_affinity) {
- cpu = cpumask_next(cpu, dev->irq_affinity);
- if (cpu >= nr_cpu_ids)
- cpu = cpumask_first(dev->irq_affinity);
- mask = cpumask_of(cpu);
- }
+ int ret, i;
+
+ if (affinity) {
+ masks = irq_create_affinity_masks(dev->irq_affinity, nvec);
+ if (!masks)
+ pr_err("Unable to allocate affinity masks, ignoring\n");
+ }
- entry = alloc_msi_entry(&dev->dev);
+ for (i = 0, curmsk = masks; i < nvec; i++) {
+ entry = alloc_msi_entry(&dev->dev, 1, curmsk);
if (!entry) {
if (!i)
iounmap(base);
else
free_msi_irqs(dev);
/* No enough memory. Don't try again */
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out;
}
entry->msi_attrib.is_msix = 1;
@@ -711,12 +721,14 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base,
entry->msi_attrib.entry_nr = i;
entry->msi_attrib.default_irq = dev->irq;
entry->mask_base = base;
- entry->nvec_used = 1;
- entry->affinity = mask;
list_add_tail(&entry->list, dev_to_msi_list(&dev->dev));
+ if (masks)
+ curmsk++;
}
-
+ ret = 0;
+out:
+ kfree(masks);
return 0;
}
@@ -745,8 +757,8 @@ static void msix_program_entries(struct pci_dev *dev,
* single MSI-X irq. A return of zero indicates the successful setup of
* requested MSI-X entries with allocated irqs or non-zero for otherwise.
**/
-static int msix_capability_init(struct pci_dev *dev,
- struct msix_entry *entries, int nvec)
+static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
+ int nvec, bool affinity)
{
int ret;
u16 control;
@@ -761,7 +773,7 @@ static int msix_capability_init(struct pci_dev *dev,
if (!base)
return -ENOMEM;
- ret = msix_setup_entries(dev, base, entries, nvec);
+ ret = msix_setup_entries(dev, base, entries, nvec, affinity);
if (ret)
return ret;
@@ -941,22 +953,8 @@ int pci_msix_vec_count(struct pci_dev *dev)
}
EXPORT_SYMBOL(pci_msix_vec_count);
-/**
- * pci_enable_msix - configure device's MSI-X capability structure
- * @dev: pointer to the pci_dev data structure of MSI-X device function
- * @entries: pointer to an array of MSI-X entries (optional)
- * @nvec: number of MSI-X irqs requested for allocation by device driver
- *
- * Setup the MSI-X capability structure of device function with the number
- * of requested irqs upon its software driver call to request for
- * MSI-X mode enabled on its hardware device function. A return of zero
- * indicates the successful configuration of MSI-X capability structure
- * with new allocated MSI-X irqs. A return of < 0 indicates a failure.
- * Or a return of > 0 indicates that driver request is exceeding the number
- * of irqs or MSI-X vectors available. Driver should use the returned value to
- * re-send its request.
- **/
-int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
+static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries,
+ int nvec, bool affinity)
{
int nr_entries;
int i, j;
@@ -988,7 +986,27 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
dev_info(&dev->dev, "can't enable MSI-X (MSI IRQ already assigned)\n");
return -EINVAL;
}
- return msix_capability_init(dev, entries, nvec);
+ return msix_capability_init(dev, entries, nvec, affinity);
+}
+
+/**
+ * pci_enable_msix - configure device's MSI-X capability structure
+ * @dev: pointer to the pci_dev data structure of MSI-X device function
+ * @entries: pointer to an array of MSI-X entries (optional)
+ * @nvec: number of MSI-X irqs requested for allocation by device driver
+ *
+ * Setup the MSI-X capability structure of device function with the number
+ * of requested irqs upon its software driver call to request for
+ * MSI-X mode enabled on its hardware device function. A return of zero
+ * indicates the successful configuration of MSI-X capability structure
+ * with new allocated MSI-X irqs. A return of < 0 indicates a failure.
+ * Or a return of > 0 indicates that driver request is exceeding the number
+ * of irqs or MSI-X vectors available. Driver should use the returned value to
+ * re-send its request.
+ **/
+int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec)
+{
+ return __pci_enable_msix(dev, entries, nvec, false);
}
EXPORT_SYMBOL(pci_enable_msix);
@@ -1041,6 +1059,7 @@ EXPORT_SYMBOL(pci_msi_enabled);
static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
unsigned int flags)
{
+ bool affinity = flags & PCI_IRQ_AFFINITY;
int nvec;
int rc;
@@ -1069,19 +1088,17 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec,
nvec = maxvec;
for (;;) {
- if (flags & PCI_IRQ_AFFINITY) {
- dev->irq_affinity = irq_create_affinity_mask(&nvec);
+ if (affinity) {
+ nvec = irq_calc_affinity_vectors(dev->irq_affinity,
+ nvec);
if (nvec < minvec)
return -ENOSPC;
}
- rc = msi_capability_init(dev, nvec);
+ rc = msi_capability_init(dev, nvec, affinity);
if (rc == 0)
return nvec;
- kfree(dev->irq_affinity);
- dev->irq_affinity = NULL;
-
if (rc < 0)
return rc;
if (rc < minvec)
@@ -1113,26 +1130,24 @@ static int __pci_enable_msix_range(struct pci_dev *dev,
struct msix_entry *entries, int minvec, int maxvec,
unsigned int flags)
{
- int nvec = maxvec;
- int rc;
+ bool affinity = flags & PCI_IRQ_AFFINITY;
+ int rc, nvec = maxvec;
if (maxvec < minvec)
return -ERANGE;
for (;;) {
- if (flags & PCI_IRQ_AFFINITY) {
- dev->irq_affinity = irq_create_affinity_mask(&nvec);
+ if (affinity) {
+ nvec = irq_calc_affinity_vectors(dev->irq_affinity,
+ nvec);
if (nvec < minvec)
return -ENOSPC;
}
- rc = pci_enable_msix(dev, entries, nvec);
+ rc = __pci_enable_msix(dev, entries, nvec, affinity);
if (rc == 0)
return nvec;
- kfree(dev->irq_affinity);
- dev->irq_affinity = NULL;
-
if (rc < 0)
return rc;
if (rc < minvec)
@@ -1256,6 +1271,37 @@ int pci_irq_vector(struct pci_dev *dev, unsigned int nr)
}
EXPORT_SYMBOL(pci_irq_vector);
+/**
+ * pci_irq_get_affinity - return the affinity of a particular msi vector
+ * @dev: PCI device to operate on
+ * @nr: device-relative interrupt vector index (0-based).
+ */
+const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr)
+{
+ if (dev->msix_enabled) {
+ struct msi_desc *entry;
+ int i = 0;
+
+ for_each_pci_msi_entry(entry, dev) {
+ if (i == nr)
+ return entry->affinity;
+ i++;
+ }
+ WARN_ON_ONCE(1);
+ return NULL;
+ } else if (dev->msi_enabled) {
+ struct msi_desc *entry = first_pci_msi_entry(dev);
+
+ if (WARN_ON_ONCE(!entry || nr >= entry->nvec_used))
+ return NULL;
+
+ return &entry->affinity[nr];
+ } else {
+ return cpu_possible_mask;
+ }
+}
+EXPORT_SYMBOL(pci_irq_get_affinity);
+
struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc)
{
return to_pci_dev(desc->dev);
@@ -1502,8 +1548,8 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
of_node = irq_domain_get_of_node(domain);
- if (of_node)
- rid = of_msi_map_rid(&pdev->dev, of_node, rid);
+ rid = of_node ? of_msi_map_rid(&pdev->dev, of_node, rid) :
+ iort_msi_map_rid(&pdev->dev, rid);
return rid;
}
@@ -1519,9 +1565,13 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
*/
struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
{
+ struct irq_domain *dom;
u32 rid = 0;
pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
- return of_msi_map_get_device_domain(&pdev->dev, rid);
+ dom = of_msi_map_get_device_domain(&pdev->dev, rid);
+ if (!dom)
+ dom = iort_get_device_domain(&pdev->dev, rid);
+ return dom;
}
#endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
diff --git a/drivers/pci/pci-mid.c b/drivers/pci/pci-mid.c
index c878aa7..55f453d 100644
--- a/drivers/pci/pci-mid.c
+++ b/drivers/pci/pci-mid.c
@@ -60,8 +60,13 @@ static struct pci_platform_pm_ops mid_pci_platform_pm = {
#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
+/*
+ * This table should be in sync with the one in
+ * arch/x86/platform/intel-mid/pwr.c.
+ */
static const struct x86_cpu_id lpss_cpu_ids[] = {
- ICPU(INTEL_FAM6_ATOM_MERRIFIELD1),
+ ICPU(INTEL_FAM6_ATOM_PENWELL),
+ ICPU(INTEL_FAM6_ATOM_MERRIFIELD),
{}
};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index aab9d51..415956c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -480,6 +480,30 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
EXPORT_SYMBOL(pci_find_parent_resource);
/**
+ * pci_find_resource - Return matching PCI device resource
+ * @dev: PCI device to query
+ * @res: Resource to look for
+ *
+ * Goes over standard PCI resources (BARs) and checks if the given resource
+ * is partially or fully contained in any of them. In that case the
+ * matching resource is returned, %NULL otherwise.
+ */
+struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res)
+{
+ int i;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++) {
+ struct resource *r = &dev->resource[i];
+
+ if (r->start && resource_contains(r, res))
+ return r;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(pci_find_resource);
+
+/**
* pci_find_pcie_root_port - return PCIe Root Port
* @dev: PCI device to query
*
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index c74059e..f30ca75 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -25,6 +25,7 @@
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
#include "pci.h"
unsigned int pci_flags;
@@ -1852,8 +1853,13 @@ void __init pci_assign_unassigned_resources(void)
{
struct pci_bus *root_bus;
- list_for_each_entry(root_bus, &pci_root_buses, node)
+ list_for_each_entry(root_bus, &pci_root_buses, node) {
pci_assign_unassigned_root_bus_resources(root_bus);
+
+ /* Make sure the root bridge has a companion ACPI device: */
+ if (ACPI_HANDLE(root_bus->bridge))
+ acpi_ioapic_add(ACPI_HANDLE(root_bus->bridge));
+ }
}
void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index f5e1008..b37b572 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -534,6 +534,24 @@ static int armpmu_filter_match(struct perf_event *event)
return cpumask_test_cpu(cpu, &armpmu->supported_cpus);
}
+static ssize_t armpmu_cpumask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(dev_get_drvdata(dev));
+ return cpumap_print_to_pagebuf(true, buf, &armpmu->supported_cpus);
+}
+
+static DEVICE_ATTR(cpus, S_IRUGO, armpmu_cpumask_show, NULL);
+
+static struct attribute *armpmu_common_attrs[] = {
+ &dev_attr_cpus.attr,
+ NULL,
+};
+
+static struct attribute_group armpmu_common_attr_group = {
+ .attrs = armpmu_common_attrs,
+};
+
static void armpmu_init(struct arm_pmu *armpmu)
{
atomic_set(&armpmu->active_events, 0);
@@ -549,7 +567,10 @@ static void armpmu_init(struct arm_pmu *armpmu)
.stop = armpmu_stop,
.read = armpmu_read,
.filter_match = armpmu_filter_match,
+ .attr_groups = armpmu->attr_groups,
};
+ armpmu->attr_groups[ARMPMU_ATTR_GROUP_COMMON] =
+ &armpmu_common_attr_group;
}
/* Set at runtime when we know what CPU type we are. */
@@ -602,7 +623,7 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
irqs = min(pmu_device->num_resources, num_possible_cpus());
irq = platform_get_irq(pmu_device, 0);
- if (irq >= 0 && irq_is_percpu(irq)) {
+ if (irq > 0 && irq_is_percpu(irq)) {
on_each_cpu_mask(&cpu_pmu->supported_cpus,
cpu_pmu_disable_percpu_irq, &irq, 1);
free_percpu_irq(irq, &hw_events->percpu_pmu);
@@ -616,7 +637,7 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
if (!cpumask_test_and_clear_cpu(cpu, &cpu_pmu->active_irqs))
continue;
irq = platform_get_irq(pmu_device, i);
- if (irq >= 0)
+ if (irq > 0)
free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu));
}
}
@@ -638,7 +659,7 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
}
irq = platform_get_irq(pmu_device, 0);
- if (irq >= 0 && irq_is_percpu(irq)) {
+ if (irq > 0 && irq_is_percpu(irq)) {
err = request_percpu_irq(irq, handler, "arm-pmu",
&hw_events->percpu_pmu);
if (err) {
@@ -688,28 +709,20 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
return 0;
}
-static DEFINE_SPINLOCK(arm_pmu_lock);
-static LIST_HEAD(arm_pmu_list);
-
/*
* PMU hardware loses all context when a CPU goes offline.
* When a CPU is hotplugged back in, since some hardware registers are
* UNKNOWN at reset, the PMU must be explicitly reset to avoid reading
* junk values out of them.
*/
-static int arm_perf_starting_cpu(unsigned int cpu)
+static int arm_perf_starting_cpu(unsigned int cpu, struct hlist_node *node)
{
- struct arm_pmu *pmu;
-
- spin_lock(&arm_pmu_lock);
- list_for_each_entry(pmu, &arm_pmu_list, entry) {
+ struct arm_pmu *pmu = hlist_entry_safe(node, struct arm_pmu, node);
- if (!cpumask_test_cpu(cpu, &pmu->supported_cpus))
- continue;
- if (pmu->reset)
- pmu->reset(pmu);
- }
- spin_unlock(&arm_pmu_lock);
+ if (!cpumask_test_cpu(cpu, &pmu->supported_cpus))
+ return 0;
+ if (pmu->reset)
+ pmu->reset(pmu);
return 0;
}
@@ -821,9 +834,10 @@ static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
if (!cpu_hw_events)
return -ENOMEM;
- spin_lock(&arm_pmu_lock);
- list_add_tail(&cpu_pmu->entry, &arm_pmu_list);
- spin_unlock(&arm_pmu_lock);
+ err = cpuhp_state_add_instance_nocalls(CPUHP_AP_PERF_ARM_STARTING,
+ &cpu_pmu->node);
+ if (err)
+ goto out_free;
err = cpu_pm_pmu_register(cpu_pmu);
if (err)
@@ -859,9 +873,9 @@ static int cpu_pmu_init(struct arm_pmu *cpu_pmu)
return 0;
out_unregister:
- spin_lock(&arm_pmu_lock);
- list_del(&cpu_pmu->entry);
- spin_unlock(&arm_pmu_lock);
+ cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_STARTING,
+ &cpu_pmu->node);
+out_free:
free_percpu(cpu_hw_events);
return err;
}
@@ -869,9 +883,8 @@ out_unregister:
static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu)
{
cpu_pm_pmu_unregister(cpu_pmu);
- spin_lock(&arm_pmu_lock);
- list_del(&cpu_pmu->entry);
- spin_unlock(&arm_pmu_lock);
+ cpuhp_state_remove_instance_nocalls(CPUHP_AP_PERF_ARM_STARTING,
+ &cpu_pmu->node);
free_percpu(cpu_pmu->hw_events);
}
@@ -919,7 +932,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
/* Check the IRQ type and prohibit a mix of PPIs and SPIs */
irq = platform_get_irq(pdev, i);
- if (irq >= 0) {
+ if (irq > 0) {
bool spi = !irq_is_percpu(irq);
if (i > 0 && spi != using_spi) {
@@ -970,7 +983,7 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
if (cpumask_weight(&pmu->supported_cpus) == 0) {
int irq = platform_get_irq(pdev, 0);
- if (irq >= 0 && irq_is_percpu(irq)) {
+ if (irq > 0 && irq_is_percpu(irq)) {
/* If using PPIs, check the affinity of the partition */
int ret;
@@ -1029,7 +1042,7 @@ int arm_pmu_device_probe(struct platform_device *pdev,
ret = of_pmu_irq_cfg(pmu);
if (!ret)
ret = init_fn(pmu);
- } else {
+ } else if (probe_table) {
cpumask_setall(&pmu->supported_cpus);
ret = probe_current_pmu(pmu, probe_table);
}
@@ -1039,6 +1052,7 @@ int arm_pmu_device_probe(struct platform_device *pdev,
goto out_free;
}
+
ret = cpu_pmu_init(pmu);
if (ret)
goto out_free;
@@ -1069,9 +1083,9 @@ static int arm_pmu_hp_init(void)
{
int ret;
- ret = cpuhp_setup_state_nocalls(CPUHP_AP_PERF_ARM_STARTING,
- "AP_PERF_ARM_STARTING",
- arm_perf_starting_cpu, NULL);
+ ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_STARTING,
+ "AP_PERF_ARM_STARTING",
+ arm_perf_starting_cpu, NULL);
if (ret)
pr_err("CPU hotplug notifier for ARM PMU could not be registered: %d\n",
ret);
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 19bff3a..fe00f91 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -24,6 +24,15 @@ config PHY_BCM_NS_USB2
Enable this to support Broadcom USB 2.0 PHY connected to the USB
controller on Northstar family.
+config PHY_BCM_NS_USB3
+ tristate "Broadcom Northstar USB 3.0 PHY Driver"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ depends on HAS_IOMEM && OF
+ select GENERIC_PHY
+ help
+ Enable this to support Broadcom USB 3.0 PHY connected to the USB
+ controller on Northstar family.
+
config PHY_BERLIN_USB
tristate "Marvell Berlin USB PHY Driver"
depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
@@ -258,7 +267,9 @@ config PHY_SUN4I_USB
depends on RESET_CONTROLLER
depends on EXTCON
depends on POWER_SUPPLY
+ depends on USB_SUPPORT
select GENERIC_PHY
+ select USB_COMMON
help
Enable this to support the transceiver that is part of Allwinner
sunxi SoCs.
@@ -358,6 +369,14 @@ config PHY_ROCKCHIP_USB
help
Enable this to support the Rockchip USB 2.0 PHY.
+config PHY_ROCKCHIP_INNO_USB2
+ tristate "Rockchip INNO USB2PHY Driver"
+ depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
+ depends on COMMON_CLK
+ select GENERIC_PHY
+ help
+ Support for Rockchip USB2.0 PHY with Innosilicon IP block.
+
config PHY_ROCKCHIP_EMMC
tristate "Rockchip EMMC PHY Driver"
depends on ARCH_ROCKCHIP && OF
@@ -372,6 +391,23 @@ config PHY_ROCKCHIP_DP
help
Enable this to support the Rockchip Display Port PHY.
+config PHY_ROCKCHIP_PCIE
+ tristate "Rockchip PCIe PHY Driver"
+ depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
+ select GENERIC_PHY
+ select MFD_SYSCON
+ help
+ Enable this to support the Rockchip PCIe PHY.
+
+config PHY_ROCKCHIP_TYPEC
+ tristate "Rockchip TYPEC PHY Driver"
+ depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
+ select EXTCON
+ select GENERIC_PHY
+ select RESET_CONTROLLER
+ help
+ Enable this to support the Rockchip USB TYPEC PHY.
+
config PHY_ST_SPEAR1310_MIPHY
tristate "ST SPEAR1310-MIPHY driver"
select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 90ae198..a534cf5 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o
+obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o
obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o
@@ -39,8 +40,11 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o
obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
+obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
+obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-bcm-ns-usb3.c b/drivers/phy/phy-bcm-ns-usb3.c
new file mode 100644
index 0000000..f420fa4
--- /dev/null
+++ b/drivers/phy/phy-bcm-ns-usb3.c
@@ -0,0 +1,274 @@
+/*
+ * Broadcom Northstar USB 3.0 PHY Driver
+ *
+ * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
+ *
+ * All magic values used for initialization (and related comments) were obtained
+ * from Broadcom's SDK:
+ * Copyright (c) Broadcom Corp, 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcma/bcma.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/slab.h>
+
+#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */
+
+enum bcm_ns_family {
+ BCM_NS_UNKNOWN,
+ BCM_NS_AX,
+ BCM_NS_BX,
+};
+
+struct bcm_ns_usb3 {
+ struct device *dev;
+ enum bcm_ns_family family;
+ void __iomem *dmp;
+ void __iomem *ccb_mii;
+ struct phy *phy;
+};
+
+static const struct of_device_id bcm_ns_usb3_id_table[] = {
+ {
+ .compatible = "brcm,ns-ax-usb3-phy",
+ .data = (int *)BCM_NS_AX,
+ },
+ {
+ .compatible = "brcm,ns-bx-usb3-phy",
+ .data = (int *)BCM_NS_BX,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table);
+
+static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr,
+ u32 mask, u32 value, unsigned long timeout)
+{
+ unsigned long deadline = jiffies + timeout;
+ u32 val;
+
+ do {
+ val = readl(addr);
+ if ((val & mask) == value)
+ return 0;
+ cpu_relax();
+ udelay(10);
+ } while (!time_after_eq(jiffies, deadline));
+
+ dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
+
+ return -EBUSY;
+}
+
+static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3)
+{
+ return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL,
+ 0x0100, 0x0000,
+ usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US));
+}
+
+static int bcm_ns_usb3_mii_mng_write32(struct bcm_ns_usb3 *usb3, u32 value)
+{
+ int err;
+
+ err = bcm_ns_usb3_mii_mng_wait_idle(usb3);
+ if (err < 0) {
+ dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value);
+ return err;
+ }
+
+ writel(value, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA);
+
+ return 0;
+}
+
+static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3)
+{
+ int err;
+
+ /* Enable MDIO. Setting MDCDIV as 26 */
+ writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL);
+
+ /* Wait for MDIO? */
+ udelay(2);
+
+ /* USB3 PLL Block */
+ err = bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
+ if (err < 0)
+ return err;
+
+ /* Assert Ana_Pllseq start */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x58061000);
+
+ /* Assert CML Divider ratio to 26 */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
+
+ /* Asserting PLL Reset */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x582ec000);
+
+ /* Deaaserting PLL Reset */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x582e8000);
+
+ /* Waiting MII Mgt interface idle */
+ bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+ /* Deasserting USB3 system reset */
+ writel(0, usb3->dmp + BCMA_RESET_CTL);
+
+ /* PLL frequency monitor enable */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x58069000);
+
+ /* PIPE Block */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8060);
+
+ /* CMPMAX & CMPMINTH setting */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x580af30d);
+
+ /* DEGLITCH MIN & MAX setting */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x580e6302);
+
+ /* TXPMD block */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
+
+ /* Enabling SSC */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
+
+ /* Waiting MII Mgt interface idle */
+ bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+ return 0;
+}
+
+static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3)
+{
+ int err;
+
+ /* Enable MDIO. Setting MDCDIV as 26 */
+ writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL);
+
+ /* Wait for MDIO? */
+ udelay(2);
+
+ /* PLL30 block */
+ err = bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
+ if (err < 0)
+ return err;
+
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
+
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x587e80e0);
+
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x580a009c);
+
+ /* Enable SSC */
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
+
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x580a21d3);
+
+ bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
+
+ /* Waiting MII Mgt interface idle */
+ bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+ /* Deasserting USB3 system reset */
+ writel(0, usb3->dmp + BCMA_RESET_CTL);
+
+ return 0;
+}
+
+static int bcm_ns_usb3_phy_init(struct phy *phy)
+{
+ struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy);
+ int err;
+
+ /* Perform USB3 system soft reset */
+ writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL);
+
+ switch (usb3->family) {
+ case BCM_NS_AX:
+ err = bcm_ns_usb3_phy_init_ns_ax(usb3);
+ break;
+ case BCM_NS_BX:
+ err = bcm_ns_usb3_phy_init_ns_bx(usb3);
+ break;
+ default:
+ WARN_ON(1);
+ err = -ENOTSUPP;
+ }
+
+ return err;
+}
+
+static const struct phy_ops ops = {
+ .init = bcm_ns_usb3_phy_init,
+ .owner = THIS_MODULE,
+};
+
+static int bcm_ns_usb3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id;
+ struct bcm_ns_usb3 *usb3;
+ struct resource *res;
+ struct phy_provider *phy_provider;
+
+ usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL);
+ if (!usb3)
+ return -ENOMEM;
+
+ usb3->dev = dev;
+
+ of_id = of_match_device(bcm_ns_usb3_id_table, dev);
+ if (!of_id)
+ return -EINVAL;
+ usb3->family = (enum bcm_ns_family)of_id->data;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp");
+ usb3->dmp = devm_ioremap_resource(dev, res);
+ if (IS_ERR(usb3->dmp)) {
+ dev_err(dev, "Failed to map DMP regs\n");
+ return PTR_ERR(usb3->dmp);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii");
+ usb3->ccb_mii = devm_ioremap_resource(dev, res);
+ if (IS_ERR(usb3->ccb_mii)) {
+ dev_err(dev, "Failed to map ChipCommon B MII regs\n");
+ return PTR_ERR(usb3->ccb_mii);
+ }
+
+ usb3->phy = devm_phy_create(dev, NULL, &ops);
+ if (IS_ERR(usb3->phy)) {
+ dev_err(dev, "Failed to create PHY\n");
+ return PTR_ERR(usb3->phy);
+ }
+
+ phy_set_drvdata(usb3->phy, usb3);
+ platform_set_drvdata(pdev, usb3);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (!IS_ERR(phy_provider))
+ dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n");
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver bcm_ns_usb3_driver = {
+ .probe = bcm_ns_usb3_probe,
+ .driver = {
+ .name = "bcm_ns_usb3",
+ .of_match_table = bcm_ns_usb3_id_table,
+ },
+};
+module_platform_driver(bcm_ns_usb3_driver);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-bcm-ns2-pcie.c b/drivers/phy/phy-bcm-ns2-pcie.c
index 9513f7a..4c7d11d 100644
--- a/drivers/phy/phy-bcm-ns2-pcie.c
+++ b/drivers/phy/phy-bcm-ns2-pcie.c
@@ -18,11 +18,6 @@
#include <linux/phy.h>
#include <linux/phy/phy.h>
-struct ns2_pci_phy {
- struct mdio_device *mdiodev;
- struct phy *phy;
-};
-
#define BLK_ADDR_REG_OFFSET 0x1f
#define PLL_AFE1_100MHZ_BLK 0x2100
#define PLL_CLK_AMP_OFFSET 0x03
@@ -30,17 +25,17 @@ struct ns2_pci_phy {
static int ns2_pci_phy_init(struct phy *p)
{
- struct ns2_pci_phy *phy = phy_get_drvdata(p);
+ struct mdio_device *mdiodev = phy_get_drvdata(p);
int rc;
/* select the AFE 100MHz block page */
- rc = mdiobus_write(phy->mdiodev->bus, phy->mdiodev->addr,
+ rc = mdiobus_write(mdiodev->bus, mdiodev->addr,
BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK);
if (rc)
goto err;
/* set the 100 MHz reference clock amplitude to 2.05 v */
- rc = mdiobus_write(phy->mdiodev->bus, phy->mdiodev->addr,
+ rc = mdiobus_write(mdiodev->bus, mdiodev->addr,
PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V);
if (rc)
goto err;
@@ -48,19 +43,19 @@ static int ns2_pci_phy_init(struct phy *p)
return 0;
err:
- dev_err(&phy->mdiodev->dev, "Error %d writing to phy\n", rc);
+ dev_err(&mdiodev->dev, "Error %d writing to phy\n", rc);
return rc;
}
-static struct phy_ops ns2_pci_phy_ops = {
+static const struct phy_ops ns2_pci_phy_ops = {
.init = ns2_pci_phy_init,
+ .owner = THIS_MODULE,
};
static int ns2_pci_phy_probe(struct mdio_device *mdiodev)
{
struct device *dev = &mdiodev->dev;
struct phy_provider *provider;
- struct ns2_pci_phy *p;
struct phy *phy;
phy = devm_phy_create(dev, dev->of_node, &ns2_pci_phy_ops);
@@ -69,16 +64,7 @@ static int ns2_pci_phy_probe(struct mdio_device *mdiodev)
return PTR_ERR(phy);
}
- p = devm_kmalloc(dev, sizeof(struct ns2_pci_phy),
- GFP_KERNEL);
- if (!p)
- return -ENOMEM;
-
- p->mdiodev = mdiodev;
- dev_set_drvdata(dev, p);
-
- p->phy = phy;
- phy_set_drvdata(phy, p);
+ phy_set_drvdata(phy, mdiodev);
provider = devm_of_phy_provider_register(&phy->dev,
of_phy_simple_xlate);
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 8eca906..a268f4d 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -357,6 +357,21 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
}
EXPORT_SYMBOL_GPL(phy_set_mode);
+int phy_reset(struct phy *phy)
+{
+ int ret;
+
+ if (!phy || !phy->ops->reset)
+ return 0;
+
+ mutex_lock(&phy->mutex);
+ ret = phy->ops->reset(phy);
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_reset);
+
/**
* _of_phy_get() - lookup and obtain a reference to a phy by phandle
* @np: device_node for which to get the phy
diff --git a/drivers/phy/phy-da8xx-usb.c b/drivers/phy/phy-da8xx-usb.c
index b2e59b6..32ae78c 100644
--- a/drivers/phy/phy-da8xx-usb.c
+++ b/drivers/phy/phy-da8xx-usb.c
@@ -154,7 +154,7 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev)
d_phy->regmap = syscon_regmap_lookup_by_compatible(
"ti,da830-cfgchip");
else
- d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon.0");
+ d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon");
if (IS_ERR(d_phy->regmap)) {
dev_err(dev, "Failed to get syscon\n");
return PTR_ERR(d_phy->regmap);
diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c
index 20696f5..07ed608 100644
--- a/drivers/phy/phy-exynos5-usbdrd.c
+++ b/drivers/phy/phy-exynos5-usbdrd.c
@@ -249,7 +249,7 @@ static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst,
static unsigned int
exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst)
{
- static u32 reg;
+ u32 reg;
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
/* restore any previous reference clock settings */
@@ -295,7 +295,7 @@ exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst)
static unsigned int
exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst)
{
- static u32 reg;
+ u32 reg;
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
/* restore any previous reference clock settings */
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
index c134989..fe909fd 100644
--- a/drivers/phy/phy-omap-usb2.c
+++ b/drivers/phy/phy-omap-usb2.c
@@ -133,11 +133,49 @@ static int omap_usb_power_on(struct phy *x)
return omap_usb_phy_power(phy, true);
}
+static int omap_usb2_disable_clocks(struct omap_usb *phy)
+{
+ clk_disable(phy->wkupclk);
+ if (!IS_ERR(phy->optclk))
+ clk_disable(phy->optclk);
+
+ return 0;
+}
+
+static int omap_usb2_enable_clocks(struct omap_usb *phy)
+{
+ int ret;
+
+ ret = clk_enable(phy->wkupclk);
+ if (ret < 0) {
+ dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
+ goto err0;
+ }
+
+ if (!IS_ERR(phy->optclk)) {
+ ret = clk_enable(phy->optclk);
+ if (ret < 0) {
+ dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
+ goto err1;
+ }
+ }
+
+ return 0;
+
+err1:
+ clk_disable(phy->wkupclk);
+
+err0:
+ return ret;
+}
+
static int omap_usb_init(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
u32 val;
+ omap_usb2_enable_clocks(phy);
+
if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
/*
*
@@ -155,8 +193,16 @@ static int omap_usb_init(struct phy *x)
return 0;
}
+static int omap_usb_exit(struct phy *x)
+{
+ struct omap_usb *phy = phy_get_drvdata(x);
+
+ return omap_usb2_disable_clocks(phy);
+}
+
static const struct phy_ops ops = {
.init = omap_usb_init,
+ .exit = omap_usb_exit,
.power_on = omap_usb_power_on,
.power_off = omap_usb_power_off,
.owner = THIS_MODULE,
@@ -376,65 +422,11 @@ static int omap_usb2_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-
-static int omap_usb2_runtime_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct omap_usb *phy = platform_get_drvdata(pdev);
-
- clk_disable(phy->wkupclk);
- if (!IS_ERR(phy->optclk))
- clk_disable(phy->optclk);
-
- return 0;
-}
-
-static int omap_usb2_runtime_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct omap_usb *phy = platform_get_drvdata(pdev);
- int ret;
-
- ret = clk_enable(phy->wkupclk);
- if (ret < 0) {
- dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
- goto err0;
- }
-
- if (!IS_ERR(phy->optclk)) {
- ret = clk_enable(phy->optclk);
- if (ret < 0) {
- dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
- goto err1;
- }
- }
-
- return 0;
-
-err1:
- clk_disable(phy->wkupclk);
-
-err0:
- return ret;
-}
-
-static const struct dev_pm_ops omap_usb2_pm_ops = {
- SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume,
- NULL)
-};
-
-#define DEV_PM_OPS (&omap_usb2_pm_ops)
-#else
-#define DEV_PM_OPS NULL
-#endif
-
static struct platform_driver omap_usb2_driver = {
.probe = omap_usb2_probe,
.remove = omap_usb2_remove,
.driver = {
.name = "omap-usb2",
- .pm = DEV_PM_OPS,
.of_match_table = omap_usb2_id_table,
},
};
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
index 107cb57..18a5b49 100644
--- a/drivers/phy/phy-qcom-ufs.c
+++ b/drivers/phy/phy-qcom-ufs.c
@@ -283,10 +283,8 @@ static int __ufs_qcom_phy_init_vreg(struct phy *phy,
err = 0;
}
snprintf(prop_name, MAX_PROP_NAME, "%s-always-on", name);
- if (of_get_property(dev->of_node, prop_name, NULL))
- vreg->is_always_on = true;
- else
- vreg->is_always_on = false;
+ vreg->is_always_on = of_property_read_bool(dev->of_node,
+ prop_name);
}
if (!strcmp(name, "vdda-pll")) {
diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c
index 31156c9..3d97ead 100644
--- a/drivers/phy/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/phy-rcar-gen3-usb2.c
@@ -280,6 +280,7 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
{ .compatible = "renesas,usb2-phy-r8a7795" },
+ { .compatible = "renesas,usb2-phy-r8a7796" },
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
{ }
};
diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
new file mode 100644
index 0000000..ac20310
--- /dev/null
+++ b/drivers/phy/phy-rockchip-inno-usb2.c
@@ -0,0 +1,707 @@
+/*
+ * Rockchip USB2.0 PHY with Innosilicon IP block driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define BIT_WRITEABLE_SHIFT 16
+#define SCHEDULE_DELAY (60 * HZ)
+
+enum rockchip_usb2phy_port_id {
+ USB2PHY_PORT_OTG,
+ USB2PHY_PORT_HOST,
+ USB2PHY_NUM_PORTS,
+};
+
+enum rockchip_usb2phy_host_state {
+ PHY_STATE_HS_ONLINE = 0,
+ PHY_STATE_DISCONNECT = 1,
+ PHY_STATE_CONNECT = 2,
+ PHY_STATE_FS_LS_ONLINE = 4,
+};
+
+struct usb2phy_reg {
+ unsigned int offset;
+ unsigned int bitend;
+ unsigned int bitstart;
+ unsigned int disable;
+ unsigned int enable;
+};
+
+/**
+ * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
+ * @phy_sus: phy suspend register.
+ * @ls_det_en: linestate detection enable register.
+ * @ls_det_st: linestate detection state register.
+ * @ls_det_clr: linestate detection clear register.
+ * @utmi_ls: utmi linestate state register.
+ * @utmi_hstdet: utmi host disconnect register.
+ */
+struct rockchip_usb2phy_port_cfg {
+ struct usb2phy_reg phy_sus;
+ struct usb2phy_reg ls_det_en;
+ struct usb2phy_reg ls_det_st;
+ struct usb2phy_reg ls_det_clr;
+ struct usb2phy_reg utmi_ls;
+ struct usb2phy_reg utmi_hstdet;
+};
+
+/**
+ * struct rockchip_usb2phy_cfg: usb-phy configuration.
+ * @reg: the address offset of grf for usb-phy config.
+ * @num_ports: specify how many ports that the phy has.
+ * @clkout_ctl: keep on/turn off output clk of phy.
+ */
+struct rockchip_usb2phy_cfg {
+ unsigned int reg;
+ unsigned int num_ports;
+ struct usb2phy_reg clkout_ctl;
+ const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS];
+};
+
+/**
+ * struct rockchip_usb2phy_port: usb-phy port data.
+ * @port_id: flag for otg port or host port.
+ * @suspended: phy suspended flag.
+ * @ls_irq: IRQ number assigned for linestate detection.
+ * @mutex: for register updating in sm_work.
+ * @sm_work: OTG state machine work.
+ * @phy_cfg: port register configuration, assigned by driver data.
+ */
+struct rockchip_usb2phy_port {
+ struct phy *phy;
+ unsigned int port_id;
+ bool suspended;
+ int ls_irq;
+ struct mutex mutex;
+ struct delayed_work sm_work;
+ const struct rockchip_usb2phy_port_cfg *port_cfg;
+};
+
+/**
+ * struct rockchip_usb2phy: usb2.0 phy driver data.
+ * @grf: General Register Files regmap.
+ * @clk: clock struct of phy input clk.
+ * @clk480m: clock struct of phy output clk.
+ * @clk_hw: clock struct of phy output clk management.
+ * @phy_cfg: phy register configuration, assigned by driver data.
+ * @ports: phy port instance.
+ */
+struct rockchip_usb2phy {
+ struct device *dev;
+ struct regmap *grf;
+ struct clk *clk;
+ struct clk *clk480m;
+ struct clk_hw clk480m_hw;
+ const struct rockchip_usb2phy_cfg *phy_cfg;
+ struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS];
+};
+
+static inline int property_enable(struct rockchip_usb2phy *rphy,
+ const struct usb2phy_reg *reg, bool en)
+{
+ unsigned int val, mask, tmp;
+
+ tmp = en ? reg->enable : reg->disable;
+ mask = GENMASK(reg->bitend, reg->bitstart);
+ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+ return regmap_write(rphy->grf, reg->offset, val);
+}
+
+static inline bool property_enabled(struct rockchip_usb2phy *rphy,
+ const struct usb2phy_reg *reg)
+{
+ int ret;
+ unsigned int tmp, orig;
+ unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
+
+ ret = regmap_read(rphy->grf, reg->offset, &orig);
+ if (ret)
+ return false;
+
+ tmp = (orig & mask) >> reg->bitstart;
+ return tmp == reg->enable;
+}
+
+static int rockchip_usb2phy_clk480m_enable(struct clk_hw *hw)
+{
+ struct rockchip_usb2phy *rphy =
+ container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+ int ret;
+
+ /* turn on 480m clk output if it is off */
+ if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) {
+ ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true);
+ if (ret)
+ return ret;
+
+ /* waitting for the clk become stable */
+ mdelay(1);
+ }
+
+ return 0;
+}
+
+static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw)
+{
+ struct rockchip_usb2phy *rphy =
+ container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+ /* turn off 480m clk output */
+ property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false);
+}
+
+static int rockchip_usb2phy_clk480m_enabled(struct clk_hw *hw)
+{
+ struct rockchip_usb2phy *rphy =
+ container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+ return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl);
+}
+
+static unsigned long
+rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return 480000000;
+}
+
+static const struct clk_ops rockchip_usb2phy_clkout_ops = {
+ .enable = rockchip_usb2phy_clk480m_enable,
+ .disable = rockchip_usb2phy_clk480m_disable,
+ .is_enabled = rockchip_usb2phy_clk480m_enabled,
+ .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate,
+};
+
+static void rockchip_usb2phy_clk480m_unregister(void *data)
+{
+ struct rockchip_usb2phy *rphy = data;
+
+ of_clk_del_provider(rphy->dev->of_node);
+ clk_unregister(rphy->clk480m);
+}
+
+static int
+rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
+{
+ struct device_node *node = rphy->dev->of_node;
+ struct clk_init_data init;
+ const char *clk_name;
+ int ret;
+
+ init.flags = 0;
+ init.name = "clk_usbphy_480m";
+ init.ops = &rockchip_usb2phy_clkout_ops;
+
+ /* optional override of the clockname */
+ of_property_read_string(node, "clock-output-names", &init.name);
+
+ if (rphy->clk) {
+ clk_name = __clk_get_name(rphy->clk);
+ init.parent_names = &clk_name;
+ init.num_parents = 1;
+ } else {
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ }
+
+ rphy->clk480m_hw.init = &init;
+
+ /* register the clock */
+ rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw);
+ if (IS_ERR(rphy->clk480m)) {
+ ret = PTR_ERR(rphy->clk480m);
+ goto err_ret;
+ }
+
+ ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m);
+ if (ret < 0)
+ goto err_clk_provider;
+
+ ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister,
+ rphy);
+ if (ret < 0)
+ goto err_unreg_action;
+
+ return 0;
+
+err_unreg_action:
+ of_clk_del_provider(node);
+err_clk_provider:
+ clk_unregister(rphy->clk480m);
+err_ret:
+ return ret;
+}
+
+static int rockchip_usb2phy_init(struct phy *phy)
+{
+ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+ struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+ int ret;
+
+ if (rport->port_id == USB2PHY_PORT_HOST) {
+ /* clear linestate and enable linestate detect irq */
+ mutex_lock(&rport->mutex);
+
+ ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+ if (ret) {
+ mutex_unlock(&rport->mutex);
+ return ret;
+ }
+
+ ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+ if (ret) {
+ mutex_unlock(&rport->mutex);
+ return ret;
+ }
+
+ mutex_unlock(&rport->mutex);
+ schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+ }
+
+ return 0;
+}
+
+static int rockchip_usb2phy_power_on(struct phy *phy)
+{
+ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+ struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+ int ret;
+
+ dev_dbg(&rport->phy->dev, "port power on\n");
+
+ if (!rport->suspended)
+ return 0;
+
+ ret = clk_prepare_enable(rphy->clk480m);
+ if (ret)
+ return ret;
+
+ ret = property_enable(rphy, &rport->port_cfg->phy_sus, false);
+ if (ret)
+ return ret;
+
+ rport->suspended = false;
+ return 0;
+}
+
+static int rockchip_usb2phy_power_off(struct phy *phy)
+{
+ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+ struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+ int ret;
+
+ dev_dbg(&rport->phy->dev, "port power off\n");
+
+ if (rport->suspended)
+ return 0;
+
+ ret = property_enable(rphy, &rport->port_cfg->phy_sus, true);
+ if (ret)
+ return ret;
+
+ rport->suspended = true;
+ clk_disable_unprepare(rphy->clk480m);
+
+ return 0;
+}
+
+static int rockchip_usb2phy_exit(struct phy *phy)
+{
+ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+
+ if (rport->port_id == USB2PHY_PORT_HOST)
+ cancel_delayed_work_sync(&rport->sm_work);
+
+ return 0;
+}
+
+static const struct phy_ops rockchip_usb2phy_ops = {
+ .init = rockchip_usb2phy_init,
+ .exit = rockchip_usb2phy_exit,
+ .power_on = rockchip_usb2phy_power_on,
+ .power_off = rockchip_usb2phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * The function manage host-phy port state and suspend/resume phy port
+ * to save power.
+ *
+ * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
+ * devices is disconnect or not. Besides, we do not need care it is FS/LS
+ * disconnected or HS disconnected, actually, we just only need get the
+ * device is disconnected at last through rearm the delayed work,
+ * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
+ *
+ * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke
+ * some clk related APIs, so do not invoke it from interrupt context directly.
+ */
+static void rockchip_usb2phy_sm_work(struct work_struct *work)
+{
+ struct rockchip_usb2phy_port *rport =
+ container_of(work, struct rockchip_usb2phy_port, sm_work.work);
+ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+ unsigned int sh = rport->port_cfg->utmi_hstdet.bitend -
+ rport->port_cfg->utmi_hstdet.bitstart + 1;
+ unsigned int ul, uhd, state;
+ unsigned int ul_mask, uhd_mask;
+ int ret;
+
+ mutex_lock(&rport->mutex);
+
+ ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul);
+ if (ret < 0)
+ goto next_schedule;
+
+ ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset,
+ &uhd);
+ if (ret < 0)
+ goto next_schedule;
+
+ uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
+ rport->port_cfg->utmi_hstdet.bitstart);
+ ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
+ rport->port_cfg->utmi_ls.bitstart);
+
+ /* stitch on utmi_ls and utmi_hstdet as phy state */
+ state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
+ (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+
+ switch (state) {
+ case PHY_STATE_HS_ONLINE:
+ dev_dbg(&rport->phy->dev, "HS online\n");
+ break;
+ case PHY_STATE_FS_LS_ONLINE:
+ /*
+ * For FS/LS device, the online state share with connect state
+ * from utmi_ls and utmi_hstdet register, so we distinguish
+ * them via suspended flag.
+ *
+ * Plus, there are two cases, one is D- Line pull-up, and D+
+ * line pull-down, the state is 4; another is D+ line pull-up,
+ * and D- line pull-down, the state is 2.
+ */
+ if (!rport->suspended) {
+ /* D- line pull-up, D+ line pull-down */
+ dev_dbg(&rport->phy->dev, "FS/LS online\n");
+ break;
+ }
+ /* fall through */
+ case PHY_STATE_CONNECT:
+ if (rport->suspended) {
+ dev_dbg(&rport->phy->dev, "Connected\n");
+ rockchip_usb2phy_power_on(rport->phy);
+ rport->suspended = false;
+ } else {
+ /* D+ line pull-up, D- line pull-down */
+ dev_dbg(&rport->phy->dev, "FS/LS online\n");
+ }
+ break;
+ case PHY_STATE_DISCONNECT:
+ if (!rport->suspended) {
+ dev_dbg(&rport->phy->dev, "Disconnected\n");
+ rockchip_usb2phy_power_off(rport->phy);
+ rport->suspended = true;
+ }
+
+ /*
+ * activate the linestate detection to get the next device
+ * plug-in irq.
+ */
+ property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+ property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+
+ /*
+ * we don't need to rearm the delayed work when the phy port
+ * is suspended.
+ */
+ mutex_unlock(&rport->mutex);
+ return;
+ default:
+ dev_dbg(&rport->phy->dev, "unknown phy state\n");
+ break;
+ }
+
+next_schedule:
+ mutex_unlock(&rport->mutex);
+ schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+}
+
+static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
+{
+ struct rockchip_usb2phy_port *rport = data;
+ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+
+ if (!property_enabled(rphy, &rport->port_cfg->ls_det_st))
+ return IRQ_NONE;
+
+ mutex_lock(&rport->mutex);
+
+ /* disable linestate detect irq and clear its status */
+ property_enable(rphy, &rport->port_cfg->ls_det_en, false);
+ property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+
+ mutex_unlock(&rport->mutex);
+
+ /*
+ * In this case for host phy port, a new device is plugged in,
+ * meanwhile, if the phy port is suspended, we need rearm the work to
+ * resume it and mange its states; otherwise, we do nothing about that.
+ */
+ if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST)
+ rockchip_usb2phy_sm_work(&rport->sm_work.work);
+
+ return IRQ_HANDLED;
+}
+
+static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
+ struct rockchip_usb2phy_port *rport,
+ struct device_node *child_np)
+{
+ int ret;
+
+ rport->port_id = USB2PHY_PORT_HOST;
+ rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST];
+ rport->suspended = true;
+
+ mutex_init(&rport->mutex);
+ INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
+
+ rport->ls_irq = of_irq_get_byname(child_np, "linestate");
+ if (rport->ls_irq < 0) {
+ dev_err(rphy->dev, "no linestate irq provided\n");
+ return rport->ls_irq;
+ }
+
+ ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
+ rockchip_usb2phy_linestate_irq,
+ IRQF_ONESHOT,
+ "rockchip_usb2phy", rport);
+ if (ret) {
+ dev_err(rphy->dev, "failed to request irq handle\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_usb2phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *child_np;
+ struct phy_provider *provider;
+ struct rockchip_usb2phy *rphy;
+ const struct rockchip_usb2phy_cfg *phy_cfgs;
+ const struct of_device_id *match;
+ unsigned int reg;
+ int index, ret;
+
+ rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
+ if (!rphy)
+ return -ENOMEM;
+
+ match = of_match_device(dev->driver->of_match_table, dev);
+ if (!match || !match->data) {
+ dev_err(dev, "phy configs are not assigned!\n");
+ return -EINVAL;
+ }
+
+ if (!dev->parent || !dev->parent->of_node)
+ return -EINVAL;
+
+ rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(rphy->grf))
+ return PTR_ERR(rphy->grf);
+
+ if (of_property_read_u32(np, "reg", &reg)) {
+ dev_err(dev, "the reg property is not assigned in %s node\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ rphy->dev = dev;
+ phy_cfgs = match->data;
+ platform_set_drvdata(pdev, rphy);
+
+ /* find out a proper config which can be matched with dt. */
+ index = 0;
+ while (phy_cfgs[index].reg) {
+ if (phy_cfgs[index].reg == reg) {
+ rphy->phy_cfg = &phy_cfgs[index];
+ break;
+ }
+
+ ++index;
+ }
+
+ if (!rphy->phy_cfg) {
+ dev_err(dev, "no phy-config can be matched with %s node\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ rphy->clk = of_clk_get_by_name(np, "phyclk");
+ if (!IS_ERR(rphy->clk)) {
+ clk_prepare_enable(rphy->clk);
+ } else {
+ dev_info(&pdev->dev, "no phyclk specified\n");
+ rphy->clk = NULL;
+ }
+
+ ret = rockchip_usb2phy_clk480m_register(rphy);
+ if (ret) {
+ dev_err(dev, "failed to register 480m output clock\n");
+ goto disable_clks;
+ }
+
+ index = 0;
+ for_each_available_child_of_node(np, child_np) {
+ struct rockchip_usb2phy_port *rport = &rphy->ports[index];
+ struct phy *phy;
+
+ /*
+ * This driver aim to support both otg-port and host-port,
+ * but unfortunately, the otg part is not ready in current,
+ * so this comments and below codes are interim, which should
+ * be changed after otg-port is supplied soon.
+ */
+ if (of_node_cmp(child_np->name, "host-port"))
+ goto next_child;
+
+ phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create phy\n");
+ ret = PTR_ERR(phy);
+ goto put_child;
+ }
+
+ rport->phy = phy;
+ phy_set_drvdata(rport->phy, rport);
+
+ ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np);
+ if (ret)
+ goto put_child;
+
+next_child:
+ /* to prevent out of boundary */
+ if (++index >= rphy->phy_cfg->num_ports)
+ break;
+ }
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ return PTR_ERR_OR_ZERO(provider);
+
+put_child:
+ of_node_put(child_np);
+disable_clks:
+ if (rphy->clk) {
+ clk_disable_unprepare(rphy->clk);
+ clk_put(rphy->clk);
+ }
+ return ret;
+}
+
+static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
+ {
+ .reg = 0x700,
+ .num_ports = 2,
+ .clkout_ctl = { 0x0724, 15, 15, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 },
+ .ls_det_en = { 0x0680, 4, 4, 0, 1 },
+ .ls_det_st = { 0x0690, 4, 4, 0, 1 },
+ .ls_det_clr = { 0x06a0, 4, 4, 0, 1 },
+ .utmi_ls = { 0x049c, 14, 13, 0, 1 },
+ .utmi_hstdet = { 0x049c, 12, 12, 0, 1 }
+ }
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
+ {
+ .reg = 0xe450,
+ .num_ports = 2,
+ .clkout_ctl = { 0xe450, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 },
+ .ls_det_en = { 0xe3c0, 6, 6, 0, 1 },
+ .ls_det_st = { 0xe3e0, 6, 6, 0, 1 },
+ .ls_det_clr = { 0xe3d0, 6, 6, 0, 1 },
+ .utmi_ls = { 0xe2ac, 22, 21, 0, 1 },
+ .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 }
+ }
+ },
+ },
+ {
+ .reg = 0xe460,
+ .num_ports = 2,
+ .clkout_ctl = { 0xe460, 4, 4, 1, 0 },
+ .port_cfgs = {
+ [USB2PHY_PORT_HOST] = {
+ .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 },
+ .ls_det_en = { 0xe3c0, 11, 11, 0, 1 },
+ .ls_det_st = { 0xe3e0, 11, 11, 0, 1 },
+ .ls_det_clr = { 0xe3d0, 11, 11, 0, 1 },
+ .utmi_ls = { 0xe2ac, 26, 25, 0, 1 },
+ .utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 }
+ }
+ },
+ },
+ { /* sentinel */ }
+};
+
+static const struct of_device_id rockchip_usb2phy_dt_match[] = {
+ { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
+ { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match);
+
+static struct platform_driver rockchip_usb2phy_driver = {
+ .probe = rockchip_usb2phy_probe,
+ .driver = {
+ .name = "rockchip-usb2phy",
+ .of_match_table = rockchip_usb2phy_dt_match,
+ },
+};
+module_platform_driver(rockchip_usb2phy_driver);
+
+MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-rockchip-pcie.c b/drivers/phy/phy-rockchip-pcie.c
new file mode 100644
index 0000000..a2b4c6b
--- /dev/null
+++ b/drivers/phy/phy-rockchip-pcie.c
@@ -0,0 +1,357 @@
+/*
+ * Rockchip PCIe PHY driver
+ *
+ * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+ */
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+#define PHY_MAX_LANE_NUM 4
+#define PHY_CFG_DATA_SHIFT 7
+#define PHY_CFG_ADDR_SHIFT 1
+#define PHY_CFG_DATA_MASK 0xf
+#define PHY_CFG_ADDR_MASK 0x3f
+#define PHY_CFG_RD_MASK 0x3ff
+#define PHY_CFG_WR_ENABLE 1
+#define PHY_CFG_WR_DISABLE 1
+#define PHY_CFG_WR_SHIFT 0
+#define PHY_CFG_WR_MASK 1
+#define PHY_CFG_PLL_LOCK 0x10
+#define PHY_CFG_CLK_TEST 0x10
+#define PHY_CFG_CLK_SCC 0x12
+#define PHY_CFG_SEPE_RATE BIT(3)
+#define PHY_CFG_PLL_100M BIT(3)
+#define PHY_PLL_LOCKED BIT(9)
+#define PHY_PLL_OUTPUT BIT(10)
+#define PHY_LANE_A_STATUS 0x30
+#define PHY_LANE_B_STATUS 0x31
+#define PHY_LANE_C_STATUS 0x32
+#define PHY_LANE_D_STATUS 0x33
+#define PHY_LANE_RX_DET_SHIFT 11
+#define PHY_LANE_RX_DET_TH 0x1
+#define PHY_LANE_IDLE_OFF 0x1
+#define PHY_LANE_IDLE_MASK 0x1
+#define PHY_LANE_IDLE_A_SHIFT 3
+#define PHY_LANE_IDLE_B_SHIFT 4
+#define PHY_LANE_IDLE_C_SHIFT 5
+#define PHY_LANE_IDLE_D_SHIFT 6
+
+struct rockchip_pcie_data {
+ unsigned int pcie_conf;
+ unsigned int pcie_status;
+ unsigned int pcie_laneoff;
+};
+
+struct rockchip_pcie_phy {
+ struct rockchip_pcie_data *phy_data;
+ struct regmap *reg_base;
+ struct reset_control *phy_rst;
+ struct clk *clk_pciephy_ref;
+};
+
+static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy,
+ u32 addr, u32 data)
+{
+ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+ HIWORD_UPDATE(data,
+ PHY_CFG_DATA_MASK,
+ PHY_CFG_DATA_SHIFT) |
+ HIWORD_UPDATE(addr,
+ PHY_CFG_ADDR_MASK,
+ PHY_CFG_ADDR_SHIFT));
+ udelay(1);
+ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+ HIWORD_UPDATE(PHY_CFG_WR_ENABLE,
+ PHY_CFG_WR_MASK,
+ PHY_CFG_WR_SHIFT));
+ udelay(1);
+ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+ HIWORD_UPDATE(PHY_CFG_WR_DISABLE,
+ PHY_CFG_WR_MASK,
+ PHY_CFG_WR_SHIFT));
+}
+
+static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy,
+ u32 addr)
+{
+ u32 val;
+
+ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+ HIWORD_UPDATE(addr,
+ PHY_CFG_RD_MASK,
+ PHY_CFG_ADDR_SHIFT));
+ regmap_read(rk_phy->reg_base,
+ rk_phy->phy_data->pcie_status,
+ &val);
+ return val;
+}
+
+static int rockchip_pcie_phy_power_off(struct phy *phy)
+{
+ struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+ int err = 0;
+
+ err = reset_control_assert(rk_phy->phy_rst);
+ if (err) {
+ dev_err(&phy->dev, "assert phy_rst err %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int rockchip_pcie_phy_power_on(struct phy *phy)
+{
+ struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+ int err = 0;
+ u32 status;
+ unsigned long timeout;
+
+ err = reset_control_deassert(rk_phy->phy_rst);
+ if (err) {
+ dev_err(&phy->dev, "deassert phy_rst err %d\n", err);
+ return err;
+ }
+
+ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+ HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
+ PHY_CFG_ADDR_MASK,
+ PHY_CFG_ADDR_SHIFT));
+
+ /*
+ * No documented timeout value for phy operation below,
+ * so we make it large enough here. And we use loop-break
+ * method which should not be harmful.
+ */
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ err = -EINVAL;
+ while (time_before(jiffies, timeout)) {
+ regmap_read(rk_phy->reg_base,
+ rk_phy->phy_data->pcie_status,
+ &status);
+ if (status & PHY_PLL_LOCKED) {
+ dev_dbg(&phy->dev, "pll locked!\n");
+ err = 0;
+ break;
+ }
+ msleep(20);
+ }
+
+ if (err) {
+ dev_err(&phy->dev, "pll lock timeout!\n");
+ goto err_pll_lock;
+ }
+
+ phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
+ phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
+
+ err = -ETIMEDOUT;
+ while (time_before(jiffies, timeout)) {
+ regmap_read(rk_phy->reg_base,
+ rk_phy->phy_data->pcie_status,
+ &status);
+ if (!(status & PHY_PLL_OUTPUT)) {
+ dev_dbg(&phy->dev, "pll output enable done!\n");
+ err = 0;
+ break;
+ }
+ msleep(20);
+ }
+
+ if (err) {
+ dev_err(&phy->dev, "pll output enable timeout!\n");
+ goto err_pll_lock;
+ }
+
+ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+ HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
+ PHY_CFG_ADDR_MASK,
+ PHY_CFG_ADDR_SHIFT));
+ err = -EINVAL;
+ while (time_before(jiffies, timeout)) {
+ regmap_read(rk_phy->reg_base,
+ rk_phy->phy_data->pcie_status,
+ &status);
+ if (status & PHY_PLL_LOCKED) {
+ dev_dbg(&phy->dev, "pll relocked!\n");
+ err = 0;
+ break;
+ }
+ msleep(20);
+ }
+
+ if (err) {
+ dev_err(&phy->dev, "pll relock timeout!\n");
+ goto err_pll_lock;
+ }
+
+ return 0;
+
+err_pll_lock:
+ reset_control_assert(rk_phy->phy_rst);
+ return err;
+}
+
+static int rockchip_pcie_phy_init(struct phy *phy)
+{
+ struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+ int err = 0;
+
+ err = clk_prepare_enable(rk_phy->clk_pciephy_ref);
+ if (err) {
+ dev_err(&phy->dev, "Fail to enable pcie ref clock.\n");
+ goto err_refclk;
+ }
+
+ err = reset_control_assert(rk_phy->phy_rst);
+ if (err) {
+ dev_err(&phy->dev, "assert phy_rst err %d\n", err);
+ goto err_reset;
+ }
+
+ return err;
+
+err_reset:
+ clk_disable_unprepare(rk_phy->clk_pciephy_ref);
+err_refclk:
+ return err;
+}
+
+static int rockchip_pcie_phy_exit(struct phy *phy)
+{
+ struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+ int err = 0;
+
+ clk_disable_unprepare(rk_phy->clk_pciephy_ref);
+
+ err = reset_control_deassert(rk_phy->phy_rst);
+ if (err) {
+ dev_err(&phy->dev, "deassert phy_rst err %d\n", err);
+ goto err_reset;
+ }
+
+ return err;
+
+err_reset:
+ clk_prepare_enable(rk_phy->clk_pciephy_ref);
+ return err;
+}
+
+static const struct phy_ops ops = {
+ .init = rockchip_pcie_phy_init,
+ .exit = rockchip_pcie_phy_exit,
+ .power_on = rockchip_pcie_phy_power_on,
+ .power_off = rockchip_pcie_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static const struct rockchip_pcie_data rk3399_pcie_data = {
+ .pcie_conf = 0xe220,
+ .pcie_status = 0xe2a4,
+ .pcie_laneoff = 0xe214,
+};
+
+static const struct of_device_id rockchip_pcie_phy_dt_ids[] = {
+ {
+ .compatible = "rockchip,rk3399-pcie-phy",
+ .data = &rk3399_pcie_data,
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids);
+
+static int rockchip_pcie_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rockchip_pcie_phy *rk_phy;
+ struct phy *generic_phy;
+ struct phy_provider *phy_provider;
+ struct regmap *grf;
+ const struct of_device_id *of_id;
+
+ grf = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(grf)) {
+ dev_err(dev, "Cannot find GRF syscon\n");
+ return PTR_ERR(grf);
+ }
+
+ rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
+ if (!rk_phy)
+ return -ENOMEM;
+
+ of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev);
+ if (!of_id)
+ return -EINVAL;
+
+ rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data;
+ rk_phy->reg_base = grf;
+
+ rk_phy->phy_rst = devm_reset_control_get(dev, "phy");
+ if (IS_ERR(rk_phy->phy_rst)) {
+ if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER)
+ dev_err(dev,
+ "missing phy property for reset controller\n");
+ return PTR_ERR(rk_phy->phy_rst);
+ }
+
+ rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk");
+ if (IS_ERR(rk_phy->clk_pciephy_ref)) {
+ dev_err(dev, "refclk not found.\n");
+ return PTR_ERR(rk_phy->clk_pciephy_ref);
+ }
+
+ generic_phy = devm_phy_create(dev, dev->of_node, &ops);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(generic_phy);
+ }
+
+ phy_set_drvdata(generic_phy, rk_phy);
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver rockchip_pcie_driver = {
+ .probe = rockchip_pcie_phy_probe,
+ .driver = {
+ .name = "rockchip-pcie-phy",
+ .of_match_table = rockchip_pcie_phy_dt_ids,
+ },
+};
+
+module_platform_driver(rockchip_pcie_driver);
+
+MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip PCIe PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
new file mode 100644
index 0000000..7cfb0f8
--- /dev/null
+++ b/drivers/phy/phy-rockchip-typec.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ * Kever Yang <kever.yang@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * The ROCKCHIP Type-C PHY has two PLL clocks. The first PLL clock
+ * is used for USB3, the second PLL clock is used for DP. This Type-C PHY has
+ * 3 working modes: USB3 only mode, DP only mode, and USB3+DP mode.
+ * At USB3 only mode, both PLL clocks need to be initialized, this allows the
+ * PHY to switch mode between USB3 and USB3+DP, without disconnecting the USB
+ * device.
+ * In The DP only mode, only the DP PLL needs to be powered on, and the 4 lanes
+ * are all used for DP.
+ *
+ * This driver gets extcon cable state and property, then decides which mode to
+ * select:
+ *
+ * 1. USB3 only mode:
+ * EXTCON_USB or EXTCON_USB_HOST state is true, and
+ * EXTCON_PROP_USB_SS property is true.
+ * EXTCON_DISP_DP state is false.
+ *
+ * 2. DP only mode:
+ * EXTCON_DISP_DP state is true, and
+ * EXTCON_PROP_USB_SS property is false.
+ * If EXTCON_USB_HOST state is true, it is DP + USB2 mode, since the USB2 phy
+ * is a separate phy, so this case is still DP only mode.
+ *
+ * 3. USB3+DP mode:
+ * EXTCON_USB_HOST and EXTCON_DISP_DP are both true, and
+ * EXTCON_PROP_USB_SS property is true.
+ *
+ * This Type-C PHY driver supports normal and flip orientation. The orientation
+ * is reported by the EXTCON_PROP_USB_TYPEC_POLARITY property: true is flip
+ * orientation, false is normal orientation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#define CMN_SSM_BANDGAP (0x21 << 2)
+#define CMN_SSM_BIAS (0x22 << 2)
+#define CMN_PLLSM0_PLLEN (0x29 << 2)
+#define CMN_PLLSM0_PLLPRE (0x2a << 2)
+#define CMN_PLLSM0_PLLVREF (0x2b << 2)
+#define CMN_PLLSM0_PLLLOCK (0x2c << 2)
+#define CMN_PLLSM1_PLLEN (0x31 << 2)
+#define CMN_PLLSM1_PLLPRE (0x32 << 2)
+#define CMN_PLLSM1_PLLVREF (0x33 << 2)
+#define CMN_PLLSM1_PLLLOCK (0x34 << 2)
+#define CMN_PLLSM1_USER_DEF_CTRL (0x37 << 2)
+#define CMN_ICAL_OVRD (0xc1 << 2)
+#define CMN_PLL0_VCOCAL_OVRD (0x83 << 2)
+#define CMN_PLL0_VCOCAL_INIT (0x84 << 2)
+#define CMN_PLL0_VCOCAL_ITER (0x85 << 2)
+#define CMN_PLL0_LOCK_REFCNT_START (0x90 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_START (0x92 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_THR (0x93 << 2)
+#define CMN_PLL0_INTDIV (0x94 << 2)
+#define CMN_PLL0_FRACDIV (0x95 << 2)
+#define CMN_PLL0_HIGH_THR (0x96 << 2)
+#define CMN_PLL0_DSM_DIAG (0x97 << 2)
+#define CMN_PLL0_SS_CTRL1 (0x98 << 2)
+#define CMN_PLL0_SS_CTRL2 (0x99 << 2)
+#define CMN_PLL1_VCOCAL_START (0xa1 << 2)
+#define CMN_PLL1_VCOCAL_OVRD (0xa3 << 2)
+#define CMN_PLL1_VCOCAL_INIT (0xa4 << 2)
+#define CMN_PLL1_VCOCAL_ITER (0xa5 << 2)
+#define CMN_PLL1_LOCK_REFCNT_START (0xb0 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_START (0xb2 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_THR (0xb3 << 2)
+#define CMN_PLL1_INTDIV (0xb4 << 2)
+#define CMN_PLL1_FRACDIV (0xb5 << 2)
+#define CMN_PLL1_HIGH_THR (0xb6 << 2)
+#define CMN_PLL1_DSM_DIAG (0xb7 << 2)
+#define CMN_PLL1_SS_CTRL1 (0xb8 << 2)
+#define CMN_PLL1_SS_CTRL2 (0xb9 << 2)
+#define CMN_RXCAL_OVRD (0xd1 << 2)
+#define CMN_TXPUCAL_CTRL (0xe0 << 2)
+#define CMN_TXPUCAL_OVRD (0xe1 << 2)
+#define CMN_TXPDCAL_OVRD (0xf1 << 2)
+#define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2)
+#define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2)
+#define CMN_DIAG_PLL0_OVRD (0x1c2 << 2)
+#define CMN_DIAG_PLL0_V2I_TUNE (0x1c5 << 2)
+#define CMN_DIAG_PLL0_CP_TUNE (0x1c6 << 2)
+#define CMN_DIAG_PLL0_LF_PROG (0x1c7 << 2)
+#define CMN_DIAG_PLL1_FBH_OVRD (0x1d0 << 2)
+#define CMN_DIAG_PLL1_FBL_OVRD (0x1d1 << 2)
+#define CMN_DIAG_PLL1_OVRD (0x1d2 << 2)
+#define CMN_DIAG_PLL1_V2I_TUNE (0x1d5 << 2)
+#define CMN_DIAG_PLL1_CP_TUNE (0x1d6 << 2)
+#define CMN_DIAG_PLL1_LF_PROG (0x1d7 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE1 (0x1d8 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE2 (0x1d9 << 2)
+#define CMN_DIAG_PLL1_INCLK_CTRL (0x1da << 2)
+#define CMN_DIAG_HSCLK_SEL (0x1e0 << 2)
+
+#define XCVR_PSM_RCTRL(n) ((0x4001 | ((n) << 9)) << 2)
+#define XCVR_PSM_CAL_TMR(n) ((0x4002 | ((n) << 9)) << 2)
+#define XCVR_PSM_A0IN_TMR(n) ((0x4003 | ((n) << 9)) << 2)
+#define TX_TXCC_CAL_SCLR_MULT(n) ((0x4047 | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_00(n) ((0x404c | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_01(n) ((0x404d | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_10(n) ((0x404e | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_11(n) ((0x404f | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_000(n) ((0x4050 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_001(n) ((0x4051 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_010(n) ((0x4052 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_011(n) ((0x4053 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_100(n) ((0x4054 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2)
+#define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2)
+#define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2)
+#define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2)
+#define TX_PSC_A0(n) ((0x4100 | ((n) << 9)) << 2)
+#define TX_PSC_A1(n) ((0x4101 | ((n) << 9)) << 2)
+#define TX_PSC_A2(n) ((0x4102 | ((n) << 9)) << 2)
+#define TX_PSC_A3(n) ((0x4103 | ((n) << 9)) << 2)
+#define TX_RCVDET_CTRL(n) ((0x4120 | ((n) << 9)) << 2)
+#define TX_RCVDET_EN_TMR(n) ((0x4122 | ((n) << 9)) << 2)
+#define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2)
+#define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2)
+#define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2)
+#define TX_ANA_CTRL_REG_1 (0x5020 << 2)
+#define TX_ANA_CTRL_REG_2 (0x5021 << 2)
+#define TXDA_COEFF_CALC_CTRL (0x5022 << 2)
+#define TX_DIG_CTRL_REG_2 (0x5024 << 2)
+#define TXDA_CYA_AUXDA_CYA (0x5025 << 2)
+#define TX_ANA_CTRL_REG_3 (0x5026 << 2)
+#define TX_ANA_CTRL_REG_4 (0x5027 << 2)
+#define TX_ANA_CTRL_REG_5 (0x5029 << 2)
+
+#define RX_PSC_A0(n) ((0x8000 | ((n) << 9)) << 2)
+#define RX_PSC_A1(n) ((0x8001 | ((n) << 9)) << 2)
+#define RX_PSC_A2(n) ((0x8002 | ((n) << 9)) << 2)
+#define RX_PSC_A3(n) ((0x8003 | ((n) << 9)) << 2)
+#define RX_PSC_CAL(n) ((0x8006 | ((n) << 9)) << 2)
+#define RX_PSC_RDY(n) ((0x8007 | ((n) << 9)) << 2)
+#define RX_IQPI_ILL_CAL_OVRD (0x8023 << 2)
+#define RX_EPI_ILL_CAL_OVRD (0x8033 << 2)
+#define RX_SDCAL0_OVRD (0x8041 << 2)
+#define RX_SDCAL1_OVRD (0x8049 << 2)
+#define RX_SLC_INIT (0x806d << 2)
+#define RX_SLC_RUN (0x806e << 2)
+#define RX_CDRLF_CNFG2 (0x8081 << 2)
+#define RX_SIGDET_HL_FILT_TMR(n) ((0x8090 | ((n) << 9)) << 2)
+#define RX_SLC_IOP0_OVRD (0x8101 << 2)
+#define RX_SLC_IOP1_OVRD (0x8105 << 2)
+#define RX_SLC_QOP0_OVRD (0x8109 << 2)
+#define RX_SLC_QOP1_OVRD (0x810d << 2)
+#define RX_SLC_EOP0_OVRD (0x8111 << 2)
+#define RX_SLC_EOP1_OVRD (0x8115 << 2)
+#define RX_SLC_ION0_OVRD (0x8119 << 2)
+#define RX_SLC_ION1_OVRD (0x811d << 2)
+#define RX_SLC_QON0_OVRD (0x8121 << 2)
+#define RX_SLC_QON1_OVRD (0x8125 << 2)
+#define RX_SLC_EON0_OVRD (0x8129 << 2)
+#define RX_SLC_EON1_OVRD (0x812d << 2)
+#define RX_SLC_IEP0_OVRD (0x8131 << 2)
+#define RX_SLC_IEP1_OVRD (0x8135 << 2)
+#define RX_SLC_QEP0_OVRD (0x8139 << 2)
+#define RX_SLC_QEP1_OVRD (0x813d << 2)
+#define RX_SLC_EEP0_OVRD (0x8141 << 2)
+#define RX_SLC_EEP1_OVRD (0x8145 << 2)
+#define RX_SLC_IEN0_OVRD (0x8149 << 2)
+#define RX_SLC_IEN1_OVRD (0x814d << 2)
+#define RX_SLC_QEN0_OVRD (0x8151 << 2)
+#define RX_SLC_QEN1_OVRD (0x8155 << 2)
+#define RX_SLC_EEN0_OVRD (0x8159 << 2)
+#define RX_SLC_EEN1_OVRD (0x815d << 2)
+#define RX_REE_CTRL_DATA_MASK(n) ((0x81bb | ((n) << 9)) << 2)
+#define RX_DIAG_SIGDET_TUNE(n) ((0x81dc | ((n) << 9)) << 2)
+#define RX_DIAG_SC2C_DELAY (0x81e1 << 2)
+
+#define PMA_LANE_CFG (0xc000 << 2)
+#define PIPE_CMN_CTRL1 (0xc001 << 2)
+#define PIPE_CMN_CTRL2 (0xc002 << 2)
+#define PIPE_COM_LOCK_CFG1 (0xc003 << 2)
+#define PIPE_COM_LOCK_CFG2 (0xc004 << 2)
+#define PIPE_RCV_DET_INH (0xc005 << 2)
+#define DP_MODE_CTL (0xc008 << 2)
+#define DP_CLK_CTL (0xc009 << 2)
+#define STS (0xc00F << 2)
+#define PHY_ISO_CMN_CTRL (0xc010 << 2)
+#define PHY_DP_TX_CTL (0xc408 << 2)
+#define PMA_CMN_CTRL1 (0xc800 << 2)
+#define PHY_PMA_ISO_CMN_CTRL (0xc810 << 2)
+#define PHY_ISOLATION_CTRL (0xc81f << 2)
+#define PHY_PMA_ISO_XCVR_CTRL(n) ((0xcc11 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_LINK_MODE(n) ((0xcc12 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_PWRST_CTRL(n) ((0xcc13 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_LO(n) ((0xcc14 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_HI(n) ((0xcc15 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_LO(n) ((0xcc16 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_HI(n) ((0xcc17 | ((n) << 6)) << 2)
+#define TX_BIST_CTRL(n) ((0x4140 | ((n) << 9)) << 2)
+#define TX_BIST_UDDWR(n) ((0x4141 | ((n) << 9)) << 2)
+
+/*
+ * Selects which PLL clock will be driven on the analog high speed
+ * clock 0: PLL 0 div 1
+ * clock 1: PLL 1 div 2
+ */
+#define CLK_PLL_CONFIG 0X30
+#define CLK_PLL_MASK 0x33
+
+#define CMN_READY BIT(0)
+
+#define DP_PLL_CLOCK_ENABLE BIT(2)
+#define DP_PLL_ENABLE BIT(0)
+#define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8))
+
+#define DP_MODE_A0 BIT(4)
+#define DP_MODE_A2 BIT(6)
+#define DP_MODE_ENTER_A0 0xc101
+#define DP_MODE_ENTER_A2 0xc104
+
+#define PHY_MODE_SET_TIMEOUT 100000
+
+#define PIN_ASSIGN_C_E 0x51d9
+#define PIN_ASSIGN_D_F 0x5100
+
+#define MODE_DISCONNECT 0
+#define MODE_UFP_USB BIT(0)
+#define MODE_DFP_USB BIT(1)
+#define MODE_DFP_DP BIT(2)
+
+struct usb3phy_reg {
+ u32 offset;
+ u32 enable_bit;
+ u32 write_enable;
+};
+
+struct rockchip_usb3phy_port_cfg {
+ struct usb3phy_reg typec_conn_dir;
+ struct usb3phy_reg usb3tousb2_en;
+ struct usb3phy_reg external_psm;
+ struct usb3phy_reg pipe_status;
+};
+
+struct rockchip_typec_phy {
+ struct device *dev;
+ void __iomem *base;
+ struct extcon_dev *extcon;
+ struct regmap *grf_regs;
+ struct clk *clk_core;
+ struct clk *clk_ref;
+ struct reset_control *uphy_rst;
+ struct reset_control *pipe_rst;
+ struct reset_control *tcphy_rst;
+ struct rockchip_usb3phy_port_cfg port_cfgs;
+ /* mutex to protect access to individual PHYs */
+ struct mutex lock;
+
+ bool flip;
+ u8 mode;
+};
+
+struct phy_reg {
+ u16 value;
+ u32 addr;
+};
+
+struct phy_reg usb3_pll_cfg[] = {
+ { 0xf0, CMN_PLL0_VCOCAL_INIT },
+ { 0x18, CMN_PLL0_VCOCAL_ITER },
+ { 0xd0, CMN_PLL0_INTDIV },
+ { 0x4a4a, CMN_PLL0_FRACDIV },
+ { 0x34, CMN_PLL0_HIGH_THR },
+ { 0x1ee, CMN_PLL0_SS_CTRL1 },
+ { 0x7f03, CMN_PLL0_SS_CTRL2 },
+ { 0x20, CMN_PLL0_DSM_DIAG },
+ { 0, CMN_DIAG_PLL0_OVRD },
+ { 0, CMN_DIAG_PLL0_FBH_OVRD },
+ { 0, CMN_DIAG_PLL0_FBL_OVRD },
+ { 0x7, CMN_DIAG_PLL0_V2I_TUNE },
+ { 0x45, CMN_DIAG_PLL0_CP_TUNE },
+ { 0x8, CMN_DIAG_PLL0_LF_PROG },
+};
+
+struct phy_reg dp_pll_cfg[] = {
+ { 0xf0, CMN_PLL1_VCOCAL_INIT },
+ { 0x18, CMN_PLL1_VCOCAL_ITER },
+ { 0x30b9, CMN_PLL1_VCOCAL_START },
+ { 0x21c, CMN_PLL1_INTDIV },
+ { 0, CMN_PLL1_FRACDIV },
+ { 0x5, CMN_PLL1_HIGH_THR },
+ { 0x35, CMN_PLL1_SS_CTRL1 },
+ { 0x7f1e, CMN_PLL1_SS_CTRL2 },
+ { 0x20, CMN_PLL1_DSM_DIAG },
+ { 0, CMN_PLLSM1_USER_DEF_CTRL },
+ { 0, CMN_DIAG_PLL1_OVRD },
+ { 0, CMN_DIAG_PLL1_FBH_OVRD },
+ { 0, CMN_DIAG_PLL1_FBL_OVRD },
+ { 0x6, CMN_DIAG_PLL1_V2I_TUNE },
+ { 0x45, CMN_DIAG_PLL1_CP_TUNE },
+ { 0x8, CMN_DIAG_PLL1_LF_PROG },
+ { 0x100, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+ { 0x7, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+ { 0x4, CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
+{
+ u32 i, rdata;
+
+ /*
+ * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent
+ * cmn_psm_clk_dig_div = 2, set the clk division to 2
+ */
+ writel(0x830, tcphy->base + PMA_CMN_CTRL1);
+ for (i = 0; i < 4; i++) {
+ /*
+ * The following PHY configuration assumes a 24 MHz reference
+ * clock.
+ */
+ writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i));
+ writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i));
+ writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i));
+ }
+
+ rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+ rdata &= ~CLK_PLL_MASK;
+ rdata |= CLK_PLL_CONFIG;
+ writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
+}
+
+static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
+{
+ u32 i;
+
+ /* load the configuration of PLL0 */
+ for (i = 0; i < ARRAY_SIZE(usb3_pll_cfg); i++)
+ writel(usb3_pll_cfg[i].value,
+ tcphy->base + usb3_pll_cfg[i].addr);
+}
+
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+{
+ u32 i;
+
+ /* set the default mode to RBR */
+ writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
+ tcphy->base + DP_CLK_CTL);
+
+ /* load the configuration of PLL1 */
+ for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
+ writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+}
+
+static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+ writel(0x7799, tcphy->base + TX_PSC_A0(lane));
+ writel(0x7798, tcphy->base + TX_PSC_A1(lane));
+ writel(0x5098, tcphy->base + TX_PSC_A2(lane));
+ writel(0x5098, tcphy->base + TX_PSC_A3(lane));
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+ writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+ writel(0xa6fd, tcphy->base + RX_PSC_A0(lane));
+ writel(0xa6fd, tcphy->base + RX_PSC_A1(lane));
+ writel(0xa410, tcphy->base + RX_PSC_A2(lane));
+ writel(0x2410, tcphy->base + RX_PSC_A3(lane));
+ writel(0x23ff, tcphy->base + RX_PSC_CAL(lane));
+ writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane));
+ writel(0x03e7, tcphy->base + RX_REE_CTRL_DATA_MASK(lane));
+ writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane));
+ writel(0x2010, tcphy->base + RX_PSC_RDY(lane));
+ writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+ u16 rdata;
+
+ writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
+ writel(0x6799, tcphy->base + TX_PSC_A0(lane));
+ writel(0x6798, tcphy->base + TX_PSC_A1(lane));
+ writel(0x98, tcphy->base + TX_PSC_A2(lane));
+ writel(0x98, tcphy->base + TX_PSC_A3(lane));
+
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
+ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
+ writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
+ writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
+ writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+ writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
+
+ writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+ writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
+
+ rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+ rdata = (rdata & 0x8fff) | 0x6000;
+ writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+}
+
+static inline int property_enable(struct rockchip_typec_phy *tcphy,
+ const struct usb3phy_reg *reg, bool en)
+{
+ u32 mask = 1 << reg->write_enable;
+ u32 val = en << reg->enable_bit;
+
+ return regmap_write(tcphy->grf_regs, reg->offset, val | mask);
+}
+
+static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
+{
+ u16 rdata, rdata2, val;
+
+ /* disable txda_cal_latch_en for rewrite the calibration values */
+ rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+ val = rdata & 0xdfff;
+ writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+ /*
+ * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
+ * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
+ * works.
+ */
+ rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+ rdata = rdata & 0xffc0;
+
+ rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
+ rdata2 = rdata2 & 0x3f;
+
+ val = rdata | rdata2;
+ writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+ usleep_range(1000, 1050);
+
+ /*
+ * Enable signal for latch that sample and holds calibration values.
+ * Activate this signal for 1 clock cycle to sample new calibration
+ * values.
+ */
+ rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+ val = rdata | 0x2000;
+ writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+ usleep_range(150, 200);
+
+ /* set TX Voltage Level and TX Deemphasis to 0 */
+ writel(0, tcphy->base + PHY_DP_TX_CTL);
+ /* re-enable decap */
+ writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
+ writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
+ writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1);
+ writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1);
+
+ writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+ /*
+ * Programs txda_drv_ldo_prog[15:0], Sets driver LDO
+ * voltage 16'h1001 for DP-AUX-TX and RX
+ */
+ writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
+
+ /* re-enables Bandgap reference for LDO */
+ writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1);
+ writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1);
+
+ /*
+ * re-enables the transmitter pre-driver, driver data selection MUX,
+ * and receiver detect circuits.
+ */
+ writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
+ writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
+
+ /*
+ * BIT 12: Controls auxda_polarity, which selects the polarity of the
+ * xcvr:
+ * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull
+ * down aux_m)
+ * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down
+ * aux_p)
+ */
+ val = 0xa078;
+ if (!tcphy->flip)
+ val |= BIT(12);
+ writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+ writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
+ writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
+ writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+ /*
+ * Controls low_power_swing_en, set the voltage swing of the driver
+ * to 400mv. The values below are peak to peak (differential) values.
+ */
+ writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL);
+ writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
+
+ /* Controls tx_high_z_tm_en */
+ val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+ val |= BIT(15);
+ writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+}
+
+static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
+{
+ struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+ int ret, i;
+ u32 val;
+
+ ret = clk_prepare_enable(tcphy->clk_core);
+ if (ret) {
+ dev_err(tcphy->dev, "Failed to prepare_enable core clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(tcphy->clk_ref);
+ if (ret) {
+ dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n");
+ goto err_clk_core;
+ }
+
+ reset_control_deassert(tcphy->tcphy_rst);
+
+ property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip);
+
+ tcphy_cfg_24m(tcphy);
+
+ if (mode == MODE_DFP_DP) {
+ tcphy_cfg_dp_pll(tcphy);
+ for (i = 0; i < 4; i++)
+ tcphy_dp_cfg_lane(tcphy, i);
+
+ writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
+ } else {
+ tcphy_cfg_usb3_pll(tcphy);
+ tcphy_cfg_dp_pll(tcphy);
+ if (tcphy->flip) {
+ tcphy_tx_usb3_cfg_lane(tcphy, 3);
+ tcphy_rx_usb3_cfg_lane(tcphy, 2);
+ tcphy_dp_cfg_lane(tcphy, 0);
+ tcphy_dp_cfg_lane(tcphy, 1);
+ } else {
+ tcphy_tx_usb3_cfg_lane(tcphy, 0);
+ tcphy_rx_usb3_cfg_lane(tcphy, 1);
+ tcphy_dp_cfg_lane(tcphy, 2);
+ tcphy_dp_cfg_lane(tcphy, 3);
+ }
+
+ writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
+ }
+
+ writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+
+ reset_control_deassert(tcphy->uphy_rst);
+
+ ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1,
+ val, val & CMN_READY, 10,
+ PHY_MODE_SET_TIMEOUT);
+ if (ret < 0) {
+ dev_err(tcphy->dev, "wait pma ready timeout\n");
+ ret = -ETIMEDOUT;
+ goto err_wait_pma;
+ }
+
+ reset_control_deassert(tcphy->pipe_rst);
+
+ return 0;
+
+err_wait_pma:
+ reset_control_assert(tcphy->uphy_rst);
+ reset_control_assert(tcphy->tcphy_rst);
+ clk_disable_unprepare(tcphy->clk_ref);
+err_clk_core:
+ clk_disable_unprepare(tcphy->clk_core);
+ return ret;
+}
+
+static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy)
+{
+ reset_control_assert(tcphy->tcphy_rst);
+ reset_control_assert(tcphy->uphy_rst);
+ reset_control_assert(tcphy->pipe_rst);
+ clk_disable_unprepare(tcphy->clk_core);
+ clk_disable_unprepare(tcphy->clk_ref);
+}
+
+static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
+{
+ struct extcon_dev *edev = tcphy->extcon;
+ union extcon_property_value property;
+ unsigned int id;
+ bool dfp, ufp, dp;
+ u8 mode;
+ int ret;
+
+ ufp = extcon_get_state(edev, EXTCON_USB);
+ dfp = extcon_get_state(edev, EXTCON_USB_HOST);
+ dp = extcon_get_state(edev, EXTCON_DISP_DP);
+
+ mode = MODE_DFP_USB;
+ id = EXTCON_USB_HOST;
+
+ if (ufp) {
+ mode = MODE_UFP_USB;
+ id = EXTCON_USB;
+ } else if (dp) {
+ mode = MODE_DFP_DP;
+ id = EXTCON_DISP_DP;
+
+ ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS,
+ &property);
+ if (ret) {
+ dev_err(tcphy->dev, "get superspeed property failed\n");
+ return ret;
+ }
+
+ if (property.intval)
+ mode |= MODE_DFP_USB;
+ }
+
+ ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY,
+ &property);
+ if (ret) {
+ dev_err(tcphy->dev, "get polarity property failed\n");
+ return ret;
+ }
+
+ tcphy->flip = property.intval ? 1 : 0;
+
+ return mode;
+}
+
+static int rockchip_usb3_phy_power_on(struct phy *phy)
+{
+ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+ struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+ const struct usb3phy_reg *reg = &cfg->pipe_status;
+ int timeout, new_mode, ret = 0;
+ u32 val;
+
+ mutex_lock(&tcphy->lock);
+
+ new_mode = tcphy_get_mode(tcphy);
+ if (new_mode < 0) {
+ ret = new_mode;
+ goto unlock_ret;
+ }
+
+ /* DP-only mode; fall back to USB2 */
+ if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB)))
+ goto unlock_ret;
+
+ if (tcphy->mode == new_mode)
+ goto unlock_ret;
+
+ if (tcphy->mode == MODE_DISCONNECT)
+ tcphy_phy_init(tcphy, new_mode);
+
+ /* wait TCPHY for pipe ready */
+ for (timeout = 0; timeout < 100; timeout++) {
+ regmap_read(tcphy->grf_regs, reg->offset, &val);
+ if (!(val & BIT(reg->enable_bit))) {
+ tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB);
+ goto unlock_ret;
+ }
+ usleep_range(10, 20);
+ }
+
+ if (tcphy->mode == MODE_DISCONNECT)
+ tcphy_phy_deinit(tcphy);
+
+ ret = -ETIMEDOUT;
+
+unlock_ret:
+ mutex_unlock(&tcphy->lock);
+ return ret;
+}
+
+static int rockchip_usb3_phy_power_off(struct phy *phy)
+{
+ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+
+ mutex_lock(&tcphy->lock);
+
+ if (tcphy->mode == MODE_DISCONNECT)
+ goto unlock;
+
+ tcphy->mode &= ~(MODE_UFP_USB | MODE_DFP_USB);
+ if (tcphy->mode == MODE_DISCONNECT)
+ tcphy_phy_deinit(tcphy);
+
+unlock:
+ mutex_unlock(&tcphy->lock);
+ return 0;
+}
+
+static const struct phy_ops rockchip_usb3_phy_ops = {
+ .power_on = rockchip_usb3_phy_power_on,
+ .power_off = rockchip_usb3_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int rockchip_dp_phy_power_on(struct phy *phy)
+{
+ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+ int new_mode, ret = 0;
+ u32 val;
+
+ mutex_lock(&tcphy->lock);
+
+ new_mode = tcphy_get_mode(tcphy);
+ if (new_mode < 0) {
+ ret = new_mode;
+ goto unlock_ret;
+ }
+
+ if (!(new_mode & MODE_DFP_DP)) {
+ ret = -ENODEV;
+ goto unlock_ret;
+ }
+
+ if (tcphy->mode == new_mode)
+ goto unlock_ret;
+
+ /*
+ * If the PHY has been power on, but the mode is not DP only mode,
+ * re-init the PHY for setting all of 4 lanes to DP.
+ */
+ if (new_mode == MODE_DFP_DP && tcphy->mode != MODE_DISCONNECT) {
+ tcphy_phy_deinit(tcphy);
+ tcphy_phy_init(tcphy, new_mode);
+ } else if (tcphy->mode == MODE_DISCONNECT) {
+ tcphy_phy_init(tcphy, new_mode);
+ }
+
+ ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+ val, val & DP_MODE_A2, 1000,
+ PHY_MODE_SET_TIMEOUT);
+ if (ret < 0) {
+ dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
+ goto power_on_finish;
+ }
+
+ tcphy_dp_aux_calibration(tcphy);
+
+ writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
+
+ ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+ val, val & DP_MODE_A0, 1000,
+ PHY_MODE_SET_TIMEOUT);
+ if (ret < 0) {
+ writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+ dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
+ goto power_on_finish;
+ }
+
+ tcphy->mode |= MODE_DFP_DP;
+
+power_on_finish:
+ if (tcphy->mode == MODE_DISCONNECT)
+ tcphy_phy_deinit(tcphy);
+unlock_ret:
+ mutex_unlock(&tcphy->lock);
+ return ret;
+}
+
+static int rockchip_dp_phy_power_off(struct phy *phy)
+{
+ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+
+ mutex_lock(&tcphy->lock);
+
+ if (tcphy->mode == MODE_DISCONNECT)
+ goto unlock;
+
+ tcphy->mode &= ~MODE_DFP_DP;
+
+ writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+
+ if (tcphy->mode == MODE_DISCONNECT)
+ tcphy_phy_deinit(tcphy);
+
+unlock:
+ mutex_unlock(&tcphy->lock);
+ return 0;
+}
+
+static const struct phy_ops rockchip_dp_phy_ops = {
+ .power_on = rockchip_dp_phy_power_on,
+ .power_off = rockchip_dp_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int tcphy_get_param(struct device *dev,
+ struct usb3phy_reg *reg,
+ const char *name)
+{
+ u32 buffer[3];
+ int ret;
+
+ ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
+ if (ret) {
+ dev_err(dev, "Can not parse %s\n", name);
+ return ret;
+ }
+
+ reg->offset = buffer[0];
+ reg->enable_bit = buffer[1];
+ reg->write_enable = buffer[2];
+ return 0;
+}
+
+static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
+ struct device *dev)
+{
+ struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+ int ret;
+
+ ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
+ "rockchip,typec-conn-dir");
+ if (ret)
+ return ret;
+
+ ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
+ "rockchip,usb3tousb2-en");
+ if (ret)
+ return ret;
+
+ ret = tcphy_get_param(dev, &cfg->external_psm,
+ "rockchip,external-psm");
+ if (ret)
+ return ret;
+
+ ret = tcphy_get_param(dev, &cfg->pipe_status,
+ "rockchip,pipe-status");
+ if (ret)
+ return ret;
+
+ tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(tcphy->grf_regs)) {
+ dev_err(dev, "could not find grf dt node\n");
+ return PTR_ERR(tcphy->grf_regs);
+ }
+
+ tcphy->clk_core = devm_clk_get(dev, "tcpdcore");
+ if (IS_ERR(tcphy->clk_core)) {
+ dev_err(dev, "could not get uphy core clock\n");
+ return PTR_ERR(tcphy->clk_core);
+ }
+
+ tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref");
+ if (IS_ERR(tcphy->clk_ref)) {
+ dev_err(dev, "could not get uphy ref clock\n");
+ return PTR_ERR(tcphy->clk_ref);
+ }
+
+ tcphy->uphy_rst = devm_reset_control_get(dev, "uphy");
+ if (IS_ERR(tcphy->uphy_rst)) {
+ dev_err(dev, "no uphy_rst reset control found\n");
+ return PTR_ERR(tcphy->uphy_rst);
+ }
+
+ tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe");
+ if (IS_ERR(tcphy->pipe_rst)) {
+ dev_err(dev, "no pipe_rst reset control found\n");
+ return PTR_ERR(tcphy->pipe_rst);
+ }
+
+ tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy");
+ if (IS_ERR(tcphy->tcphy_rst)) {
+ dev_err(dev, "no tcphy_rst reset control found\n");
+ return PTR_ERR(tcphy->tcphy_rst);
+ }
+
+ return 0;
+}
+
+static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
+{
+ struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+
+ reset_control_assert(tcphy->tcphy_rst);
+ reset_control_assert(tcphy->uphy_rst);
+ reset_control_assert(tcphy->pipe_rst);
+
+ /* select external psm clock */
+ property_enable(tcphy, &cfg->external_psm, 1);
+ property_enable(tcphy, &cfg->usb3tousb2_en, 0);
+
+ tcphy->mode = MODE_DISCONNECT;
+}
+
+static int rockchip_typec_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *child_np;
+ struct rockchip_typec_phy *tcphy;
+ struct phy_provider *phy_provider;
+ struct resource *res;
+ int ret;
+
+ tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
+ if (!tcphy)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tcphy->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(tcphy->base))
+ return PTR_ERR(tcphy->base);
+
+ ret = tcphy_parse_dt(tcphy, dev);
+ if (ret)
+ return ret;
+
+ tcphy->dev = dev;
+ platform_set_drvdata(pdev, tcphy);
+ mutex_init(&tcphy->lock);
+
+ typec_phy_pre_init(tcphy);
+
+ tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(tcphy->extcon)) {
+ if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
+ dev_err(dev, "Invalid or missing extcon\n");
+ return PTR_ERR(tcphy->extcon);
+ }
+
+ pm_runtime_enable(dev);
+
+ for_each_available_child_of_node(np, child_np) {
+ struct phy *phy;
+
+ if (!of_node_cmp(child_np->name, "dp-port"))
+ phy = devm_phy_create(dev, child_np,
+ &rockchip_dp_phy_ops);
+ else if (!of_node_cmp(child_np->name, "usb3-port"))
+ phy = devm_phy_create(dev, child_np,
+ &rockchip_usb3_phy_ops);
+ else
+ continue;
+
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create phy: %s\n",
+ child_np->name);
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, tcphy);
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ dev_err(dev, "Failed to register phy provider\n");
+ return PTR_ERR(phy_provider);
+ }
+
+ return 0;
+}
+
+static int rockchip_typec_phy_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
+ { .compatible = "rockchip,rk3399-typec-phy" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
+
+static struct platform_driver rockchip_typec_phy_driver = {
+ .probe = rockchip_typec_phy_probe,
+ .remove = rockchip_typec_phy_remove,
+ .driver = {
+ .name = "rockchip-typec-phy",
+ .of_match_table = rockchip_typec_phy_dt_ids,
+ },
+};
+
+module_platform_driver(rockchip_typec_phy_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_AUTHOR("Kever Yang <kever.yang@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c
index 2a7381f..734987f 100644
--- a/drivers/phy/phy-rockchip-usb.c
+++ b/drivers/phy/phy-rockchip-usb.c
@@ -29,6 +29,7 @@
#include <linux/reset.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
+#include <linux/delay.h>
static int enable_usb_uart;
@@ -64,6 +65,7 @@ struct rockchip_usb_phy {
struct clk_hw clk480m_hw;
struct phy *phy;
bool uart_enabled;
+ struct reset_control *reset;
};
static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
@@ -144,9 +146,23 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
return clk_prepare_enable(phy->clk480m);
}
+static int rockchip_usb_phy_reset(struct phy *_phy)
+{
+ struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+
+ if (phy->reset) {
+ reset_control_assert(phy->reset);
+ udelay(10);
+ reset_control_deassert(phy->reset);
+ }
+
+ return 0;
+}
+
static const struct phy_ops ops = {
.power_on = rockchip_usb_phy_power_on,
.power_off = rockchip_usb_phy_power_off,
+ .reset = rockchip_usb_phy_reset,
.owner = THIS_MODULE,
};
@@ -185,6 +201,10 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
return -EINVAL;
}
+ rk_phy->reset = of_reset_control_get(child, "phy-reset");
+ if (IS_ERR(rk_phy->reset))
+ rk_phy->reset = NULL;
+
rk_phy->reg_offset = reg_offset;
rk_phy->clk = of_clk_get_by_name(child, "phyclk");
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
index 8c7eb33..b9342a2 100644
--- a/drivers/phy/phy-sun4i-usb.c
+++ b/drivers/phy/phy-sun4i-usb.c
@@ -40,6 +40,7 @@
#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
+#include <linux/spinlock.h>
#include <linux/usb/of.h>
#include <linux/workqueue.h>
@@ -50,7 +51,7 @@
#define REG_PHYCTL_A33 0x10
#define REG_PHY_UNK_H3 0x20
-#define REG_PMU_UNK_H3 0x10
+#define REG_PMU_UNK1 0x10
#define PHYCTL_DATA BIT(7)
@@ -98,6 +99,7 @@ enum sun4i_usb_phy_type {
sun6i_a31_phy,
sun8i_a33_phy,
sun8i_h3_phy,
+ sun50i_a64_phy,
};
struct sun4i_usb_phy_cfg {
@@ -106,13 +108,14 @@ struct sun4i_usb_phy_cfg {
u32 disc_thresh;
u8 phyctl_offset;
bool dedicated_clocks;
+ bool enable_pmu_unk1;
};
struct sun4i_usb_phy_data {
void __iomem *base;
const struct sun4i_usb_phy_cfg *cfg;
enum usb_dr_mode dr_mode;
- struct mutex mutex;
+ spinlock_t reg_lock; /* guard access to phyctl reg */
struct sun4i_usb_phy {
struct phy *phy;
void __iomem *pmu;
@@ -122,7 +125,6 @@ struct sun4i_usb_phy_data {
bool regulator_on;
int index;
} phys[MAX_PHYS];
- int first_phy;
/* phy0 / otg related variables */
struct extcon_dev *extcon;
bool phy0_init;
@@ -131,6 +133,7 @@ struct sun4i_usb_phy_data {
struct power_supply *vbus_power_supply;
struct notifier_block vbus_power_nb;
bool vbus_power_nb_registered;
+ bool force_session_end;
int id_det_irq;
int vbus_det_irq;
int id_det;
@@ -179,12 +182,14 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
u32 temp, usbc_bit = BIT(phy->index * 2);
void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
+ unsigned long flags;
int i;
- mutex_lock(&phy_data->mutex);
+ spin_lock_irqsave(&phy_data->reg_lock, flags);
- if (phy_data->cfg->type == sun8i_a33_phy) {
- /* A33 needs us to set phyctl to 0 explicitly */
+ if (phy_data->cfg->type == sun8i_a33_phy ||
+ phy_data->cfg->type == sun50i_a64_phy) {
+ /* A33 or A64 needs us to set phyctl to 0 explicitly */
writel(0, phyctl);
}
@@ -218,7 +223,8 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
data >>= 1;
}
- mutex_unlock(&phy_data->mutex);
+
+ spin_unlock_irqrestore(&phy_data->reg_lock, flags);
}
static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
@@ -258,14 +264,16 @@ static int sun4i_usb_phy_init(struct phy *_phy)
return ret;
}
+ if (data->cfg->enable_pmu_unk1) {
+ val = readl(phy->pmu + REG_PMU_UNK1);
+ writel(val & ~2, phy->pmu + REG_PMU_UNK1);
+ }
+
if (data->cfg->type == sun8i_h3_phy) {
if (phy->index == 0) {
val = readl(data->base + REG_PHY_UNK_H3);
writel(val & ~1, data->base + REG_PHY_UNK_H3);
}
-
- val = readl(phy->pmu + REG_PMU_UNK_H3);
- writel(val & ~2, phy->pmu + REG_PMU_UNK_H3);
} else {
/* Enable USB 45 Ohm resistor calibration */
if (phy->index == 0)
@@ -320,7 +328,10 @@ static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data)
{
switch (data->dr_mode) {
case USB_DR_MODE_OTG:
- return gpiod_get_value_cansleep(data->id_det_gpio);
+ if (data->id_det_gpio)
+ return gpiod_get_value_cansleep(data->id_det_gpio);
+ else
+ return 1; /* Fallback to peripheral mode */
case USB_DR_MODE_HOST:
return 0;
case USB_DR_MODE_PERIPHERAL:
@@ -382,8 +393,10 @@ static int sun4i_usb_phy_power_on(struct phy *_phy)
/* For phy0 only turn on Vbus if we don't have an ext. Vbus */
if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) &&
- data->vbus_det)
+ data->vbus_det) {
+ dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n");
return 0;
+ }
ret = regulator_enable(phy->vbus);
if (ret)
@@ -419,6 +432,35 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
return 0;
}
+static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode)
+{
+ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+ if (phy->index != 0)
+ return -EINVAL;
+
+ switch (mode) {
+ case PHY_MODE_USB_HOST:
+ data->dr_mode = USB_DR_MODE_HOST;
+ break;
+ case PHY_MODE_USB_DEVICE:
+ data->dr_mode = USB_DR_MODE_PERIPHERAL;
+ break;
+ case PHY_MODE_USB_OTG:
+ data->dr_mode = USB_DR_MODE_OTG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_info(&_phy->dev, "Changing dr_mode to %d\n", (int)data->dr_mode);
+ data->force_session_end = true;
+ queue_delayed_work(system_wq, &data->detect, 0);
+
+ return 0;
+}
+
void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
@@ -432,6 +474,7 @@ static const struct phy_ops sun4i_usb_phy_ops = {
.exit = sun4i_usb_phy_exit,
.power_on = sun4i_usb_phy_power_on,
.power_off = sun4i_usb_phy_power_off,
+ .set_mode = sun4i_usb_phy_set_mode,
.owner = THIS_MODULE,
};
@@ -440,7 +483,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
struct sun4i_usb_phy_data *data =
container_of(work, struct sun4i_usb_phy_data, detect.work);
struct phy *phy0 = data->phys[0].phy;
- int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
+ bool force_session_end, id_notify = false, vbus_notify = false;
+ int id_det, vbus_det;
if (phy0 == NULL)
return;
@@ -455,27 +499,30 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
return;
}
+ force_session_end = data->force_session_end;
+ data->force_session_end = false;
+
if (id_det != data->id_det) {
- /*
- * When a host cable (id == 0) gets plugged in on systems
- * without vbus detection report vbus low for long enough for
- * the musb-ip to end the current device session.
- */
+ /* id-change, force session end if we've no vbus detection */
if (data->dr_mode == USB_DR_MODE_OTG &&
- !sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) {
+ !sun4i_usb_phy0_have_vbus_det(data))
+ force_session_end = true;
+
+ /* When entering host mode (id = 0) force end the session now */
+ if (force_session_end && id_det == 0) {
sun4i_usb_phy0_set_vbus_detect(phy0, 0);
msleep(200);
sun4i_usb_phy0_set_vbus_detect(phy0, 1);
}
sun4i_usb_phy0_set_id_detect(phy0, id_det);
data->id_det = id_det;
- id_notify = 1;
+ id_notify = true;
}
if (vbus_det != data->vbus_det) {
sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
data->vbus_det = vbus_det;
- vbus_notify = 1;
+ vbus_notify = true;
}
mutex_unlock(&phy0->mutex);
@@ -483,13 +530,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
if (id_notify) {
extcon_set_cable_state_(data->extcon, EXTCON_USB_HOST,
!id_det);
- /*
- * When a host cable gets unplugged (id == 1) on systems
- * without vbus detection report vbus low for long enough to
- * the musb-ip to end the current host session.
- */
- if (data->dr_mode == USB_DR_MODE_OTG &&
- !sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) {
+ /* When leaving host mode force end the session here */
+ if (force_session_end && id_det == 1) {
mutex_lock(&phy0->mutex);
sun4i_usb_phy0_set_vbus_detect(phy0, 0);
msleep(1000);
@@ -534,8 +576,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
{
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
- if (args->args[0] < data->first_phy ||
- args->args[0] >= data->cfg->num_phys)
+ if (args->args[0] >= data->cfg->num_phys)
return ERR_PTR(-ENODEV);
return data->phys[args->args[0]].phy;
@@ -577,7 +618,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
- mutex_init(&data->mutex);
+ spin_lock_init(&data->reg_lock);
INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
dev_set_drvdata(dev, data);
data->cfg = of_device_get_match_data(dev);
@@ -610,33 +651,18 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
}
data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0);
- switch (data->dr_mode) {
- case USB_DR_MODE_OTG:
- /* otg without id_det makes no sense, and is not supported */
- if (!data->id_det_gpio) {
- dev_err(dev, "usb0_id_det missing or invalid\n");
- return -ENODEV;
- }
- /* fall through */
- case USB_DR_MODE_HOST:
- case USB_DR_MODE_PERIPHERAL:
- data->extcon = devm_extcon_dev_allocate(dev,
- sun4i_usb_phy0_cable);
- if (IS_ERR(data->extcon))
- return PTR_ERR(data->extcon);
- ret = devm_extcon_dev_register(dev, data->extcon);
- if (ret) {
- dev_err(dev, "failed to register extcon: %d\n", ret);
- return ret;
- }
- break;
- default:
- dev_info(dev, "dr_mode unknown, not registering usb phy0\n");
- data->first_phy = 1;
+ data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable);
+ if (IS_ERR(data->extcon))
+ return PTR_ERR(data->extcon);
+
+ ret = devm_extcon_dev_register(dev, data->extcon);
+ if (ret) {
+ dev_err(dev, "failed to register extcon: %d\n", ret);
+ return ret;
}
- for (i = data->first_phy; i < data->cfg->num_phys; i++) {
+ for (i = 0; i < data->cfg->num_phys; i++) {
struct sun4i_usb_phy *phy = data->phys + i;
char name[16];
@@ -737,6 +763,7 @@ static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
+ .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
@@ -745,6 +772,7 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
+ .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
@@ -753,6 +781,7 @@ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
+ .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
@@ -761,6 +790,7 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
.disc_thresh = 2,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = false,
+ .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
@@ -769,6 +799,7 @@ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true,
+ .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
@@ -777,6 +808,7 @@ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
+ .enable_pmu_unk1 = false,
};
static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
@@ -784,6 +816,16 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.type = sun8i_h3_phy,
.disc_thresh = 3,
.dedicated_clocks = true,
+ .enable_pmu_unk1 = true,
+};
+
+static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
+ .num_phys = 2,
+ .type = sun50i_a64_phy,
+ .disc_thresh = 3,
+ .phyctl_offset = REG_PHYCTL_A33,
+ .dedicated_clocks = true,
+ .enable_pmu_unk1 = true,
};
static const struct of_device_id sun4i_usb_phy_of_match[] = {
@@ -794,6 +836,8 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
+ { .compatible = "allwinner,sun50i-a64-usb-phy",
+ .data = &sun50i_a64_cfg},
{ },
};
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
index d9b10a3..87e6334 100644
--- a/drivers/phy/phy-twl4030-usb.c
+++ b/drivers/phy/phy-twl4030-usb.c
@@ -172,6 +172,7 @@ struct twl4030_usb {
int irq;
enum musb_vbus_id_status linkstat;
bool vbus_supplied;
+ bool musb_mailbox_pending;
struct delayed_work id_workaround_work;
};
@@ -439,6 +440,17 @@ static int __maybe_unused twl4030_usb_runtime_resume(struct device *dev)
(PHY_CLK_CTRL_CLOCKGATING_EN |
PHY_CLK_CTRL_CLK32K_EN));
+ twl4030_i2c_access(twl, 1);
+ twl4030_usb_set_mode(twl, twl->usb_mode);
+ if (twl->usb_mode == T2_USB_MODE_ULPI)
+ twl4030_i2c_access(twl, 0);
+ /*
+ * According to the TPS65950 TRM, there has to be at least 50ms
+ * delay between setting POWER_CTRL_OTG_ENAB and enabling charging
+ * so wait here so that a fully enabled phy can be expected after
+ * resume
+ */
+ msleep(50);
return 0;
}
@@ -459,11 +471,6 @@ static int twl4030_phy_power_on(struct phy *phy)
dev_dbg(twl->dev, "%s\n", __func__);
pm_runtime_get_sync(twl->dev);
- twl4030_i2c_access(twl, 1);
- twl4030_usb_set_mode(twl, twl->usb_mode);
- if (twl->usb_mode == T2_USB_MODE_ULPI)
- twl4030_i2c_access(twl, 0);
- twl->linkstat = MUSB_UNKNOWN;
schedule_delayed_work(&twl->id_workaround_work, HZ);
return 0;
@@ -569,9 +576,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
pm_runtime_mark_last_busy(twl->dev);
pm_runtime_put_autosuspend(twl->dev);
}
+ twl->musb_mailbox_pending = true;
+ }
+ if (twl->musb_mailbox_pending) {
err = musb_mailbox(status);
- if (err)
- twl->linkstat = MUSB_UNKNOWN;
+ if (!err)
+ twl->musb_mailbox_pending = false;
}
/* don't schedule during sleep - irq works right then */
@@ -676,6 +686,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
twl->irq = platform_get_irq(pdev, 0);
twl->vbus_supplied = false;
twl->linkstat = MUSB_UNKNOWN;
+ twl->musb_mailbox_pending = false;
twl->phy.dev = twl->dev;
twl->phy.label = "twl4030";
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index ec83dfd..873424a 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
+#include <linux/phy/tegra/xusb.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@@ -101,7 +102,8 @@ tegra_xusb_pad_find_phy_node(struct tegra_xusb_pad *pad, unsigned int index)
return of_find_node_by_name(np, pad->soc->lanes[index].name);
}
-int tegra_xusb_lane_lookup_function(struct tegra_xusb_lane *lane,
+static int
+tegra_xusb_lane_lookup_function(struct tegra_xusb_lane *lane,
const char *function)
{
unsigned int i;
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index b86e1bc..a511d51 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -651,11 +651,15 @@ static int ipc_create_pmc_devices(void)
{
int ret;
- ret = ipc_create_tco_device();
- if (ret) {
- dev_err(ipcdev.dev, "Failed to add tco platform device\n");
- return ret;
+ /* If we have ACPI based watchdog use that instead */
+ if (!acpi_has_watchdog()) {
+ ret = ipc_create_tco_device();
+ if (ret) {
+ dev_err(ipcdev.dev, "Failed to add tco platform device\n");
+ return ret;
+ }
}
+
ret = ipc_create_punit_device();
if (ret) {
dev_err(ipcdev.dev, "Failed to add punit platform device\n");
diff --git a/drivers/pnp/isapnp/core.c b/drivers/pnp/isapnp/core.c
index cf88f9b6..d8bf5a1 100644
--- a/drivers/pnp/isapnp/core.c
+++ b/drivers/pnp/isapnp/core.c
@@ -34,7 +34,7 @@
* 2003-08-11 Resource Management Updates - Adam Belay <ambx1@neo.rr.com>
*/
-#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
@@ -54,8 +54,6 @@ static int isapnp_rdp; /* Read Data Port */
static int isapnp_reset = 1; /* reset all PnP cards (deactivate) */
static int isapnp_verbose = 1; /* verbose mode */
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Generic ISA Plug & Play support");
module_param(isapnp_disable, int, 0);
MODULE_PARM_DESC(isapnp_disable, "ISA Plug & Play disable");
module_param(isapnp_rdp, int, 0);
@@ -64,7 +62,6 @@ module_param(isapnp_reset, int, 0);
MODULE_PARM_DESC(isapnp_reset, "ISA Plug & Play reset all cards");
module_param(isapnp_verbose, int, 0);
MODULE_PARM_DESC(isapnp_verbose, "ISA Plug & Play verbose mode");
-MODULE_LICENSE("GPL");
#define _PIDXR 0x279
#define _PNPWRP 0xa79
diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c
index db9973b..fa0f19b 100644
--- a/drivers/power/avs/smartreflex.c
+++ b/drivers/power/avs/smartreflex.c
@@ -136,7 +136,7 @@ static void sr_set_clk_length(struct omap_sr *sr)
if (IS_ERR(fck)) {
dev_err(&sr->pdev->dev, "%s: unable to get fck for device %s\n",
- __func__, dev_name(&sr->pdev->dev));
+ __func__, dev_name(&sr->pdev->dev));
return;
}
@@ -170,8 +170,8 @@ static void sr_start_vddautocomp(struct omap_sr *sr)
{
if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) {
dev_warn(&sr->pdev->dev,
- "%s: smartreflex class driver not registered\n",
- __func__);
+ "%s: smartreflex class driver not registered\n",
+ __func__);
return;
}
@@ -183,8 +183,8 @@ static void sr_stop_vddautocomp(struct omap_sr *sr)
{
if (!sr_class || !(sr_class->disable)) {
dev_warn(&sr->pdev->dev,
- "%s: smartreflex class driver not registered\n",
- __func__);
+ "%s: smartreflex class driver not registered\n",
+ __func__);
return;
}
@@ -225,9 +225,8 @@ static int sr_late_init(struct omap_sr *sr_info)
error:
list_del(&sr_info->node);
- dev_err(&sr_info->pdev->dev, "%s: ERROR in registering"
- "interrupt handler. Smartreflex will"
- "not function as desired\n", __func__);
+ dev_err(&sr_info->pdev->dev, "%s: ERROR in registering interrupt handler. Smartreflex will not function as desired\n",
+ __func__);
return ret;
}
@@ -263,7 +262,7 @@ static void sr_v1_disable(struct omap_sr *sr)
if (timeout >= SR_DISABLE_TIMEOUT)
dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n",
- __func__);
+ __func__);
/* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */
sr_modify_reg(sr, ERRCONFIG_V1, ERRCONFIG_MCUDISACKINTEN,
@@ -308,7 +307,7 @@ static void sr_v2_disable(struct omap_sr *sr)
if (timeout >= SR_DISABLE_TIMEOUT)
dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n",
- __func__);
+ __func__);
/* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */
sr_write_reg(sr, IRQENABLE_CLR, IRQENABLE_MCUDISABLEACKINT);
@@ -322,7 +321,7 @@ static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row(
if (!sr->nvalue_table) {
dev_warn(&sr->pdev->dev, "%s: Missing ntarget value table\n",
- __func__);
+ __func__);
return NULL;
}
@@ -356,8 +355,8 @@ int sr_configure_errgen(struct omap_sr *sr)
u8 senp_shift, senn_shift;
if (!sr) {
- pr_warn("%s: NULL omap_sr from %pF\n", __func__,
- (void *)_RET_IP_);
+ pr_warn("%s: NULL omap_sr from %pF\n",
+ __func__, (void *)_RET_IP_);
return -EINVAL;
}
@@ -387,8 +386,8 @@ int sr_configure_errgen(struct omap_sr *sr)
vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2;
break;
default:
- dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex"
- "module without specifying the ip\n", __func__);
+ dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n",
+ __func__);
return -EINVAL;
}
@@ -423,8 +422,8 @@ int sr_disable_errgen(struct omap_sr *sr)
u32 vpboundint_en, vpboundint_st;
if (!sr) {
- pr_warn("%s: NULL omap_sr from %pF\n", __func__,
- (void *)_RET_IP_);
+ pr_warn("%s: NULL omap_sr from %pF\n",
+ __func__, (void *)_RET_IP_);
return -EINVAL;
}
@@ -440,8 +439,8 @@ int sr_disable_errgen(struct omap_sr *sr)
vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2;
break;
default:
- dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex"
- "module without specifying the ip\n", __func__);
+ dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n",
+ __func__);
return -EINVAL;
}
@@ -478,8 +477,8 @@ int sr_configure_minmax(struct omap_sr *sr)
u8 senp_shift, senn_shift;
if (!sr) {
- pr_warn("%s: NULL omap_sr from %pF\n", __func__,
- (void *)_RET_IP_);
+ pr_warn("%s: NULL omap_sr from %pF\n",
+ __func__, (void *)_RET_IP_);
return -EINVAL;
}
@@ -504,8 +503,8 @@ int sr_configure_minmax(struct omap_sr *sr)
senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT;
break;
default:
- dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex"
- "module without specifying the ip\n", __func__);
+ dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n",
+ __func__);
return -EINVAL;
}
@@ -537,8 +536,8 @@ int sr_configure_minmax(struct omap_sr *sr)
IRQENABLE_MCUBOUNDSINT | IRQENABLE_MCUDISABLEACKINT);
break;
default:
- dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex"
- "module without specifying the ip\n", __func__);
+ dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex module without specifying the ip\n",
+ __func__);
return -EINVAL;
}
@@ -563,16 +562,16 @@ int sr_enable(struct omap_sr *sr, unsigned long volt)
int ret;
if (!sr) {
- pr_warn("%s: NULL omap_sr from %pF\n", __func__,
- (void *)_RET_IP_);
+ pr_warn("%s: NULL omap_sr from %pF\n",
+ __func__, (void *)_RET_IP_);
return -EINVAL;
}
volt_data = omap_voltage_get_voltdata(sr->voltdm, volt);
if (IS_ERR(volt_data)) {
- dev_warn(&sr->pdev->dev, "%s: Unable to get voltage table"
- "for nominal voltage %ld\n", __func__, volt);
+ dev_warn(&sr->pdev->dev, "%s: Unable to get voltage table for nominal voltage %ld\n",
+ __func__, volt);
return PTR_ERR(volt_data);
}
@@ -615,8 +614,8 @@ int sr_enable(struct omap_sr *sr, unsigned long volt)
void sr_disable(struct omap_sr *sr)
{
if (!sr) {
- pr_warn("%s: NULL omap_sr from %pF\n", __func__,
- (void *)_RET_IP_);
+ pr_warn("%s: NULL omap_sr from %pF\n",
+ __func__, (void *)_RET_IP_);
return;
}
@@ -658,13 +657,13 @@ int sr_register_class(struct omap_sr_class_data *class_data)
struct omap_sr *sr_info;
if (!class_data) {
- pr_warning("%s:, Smartreflex class data passed is NULL\n",
+ pr_warn("%s:, Smartreflex class data passed is NULL\n",
__func__);
return -EINVAL;
}
if (sr_class) {
- pr_warning("%s: Smartreflex class driver already registered\n",
+ pr_warn("%s: Smartreflex class driver already registered\n",
__func__);
return -EBUSY;
}
@@ -696,7 +695,7 @@ void omap_sr_enable(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
- pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
+ pr_warn("%s: omap_sr struct for voltdm not found\n", __func__);
return;
}
@@ -704,8 +703,8 @@ void omap_sr_enable(struct voltagedomain *voltdm)
return;
if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) {
- dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not"
- "registered\n", __func__);
+ dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n",
+ __func__);
return;
}
@@ -728,7 +727,7 @@ void omap_sr_disable(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
- pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
+ pr_warn("%s: omap_sr struct for voltdm not found\n", __func__);
return;
}
@@ -736,8 +735,8 @@ void omap_sr_disable(struct voltagedomain *voltdm)
return;
if (!sr_class || !(sr_class->disable)) {
- dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not"
- "registered\n", __func__);
+ dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n",
+ __func__);
return;
}
@@ -760,7 +759,7 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm)
struct omap_sr *sr = _sr_lookup(voltdm);
if (IS_ERR(sr)) {
- pr_warning("%s: omap_sr struct for voltdm not found\n", __func__);
+ pr_warn("%s: omap_sr struct for voltdm not found\n", __func__);
return;
}
@@ -768,8 +767,8 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm)
return;
if (!sr_class || !(sr_class->disable)) {
- dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not"
- "registered\n", __func__);
+ dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not registered\n",
+ __func__);
return;
}
@@ -787,8 +786,8 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm)
void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data)
{
if (!pmic_data) {
- pr_warning("%s: Trying to register NULL PMIC data structure"
- "with smartreflex\n", __func__);
+ pr_warn("%s: Trying to register NULL PMIC data structure with smartreflex\n",
+ __func__);
return;
}
@@ -801,7 +800,7 @@ static int omap_sr_autocomp_show(void *data, u64 *val)
struct omap_sr *sr_info = data;
if (!sr_info) {
- pr_warning("%s: omap_sr struct not found\n", __func__);
+ pr_warn("%s: omap_sr struct not found\n", __func__);
return -EINVAL;
}
@@ -815,13 +814,13 @@ static int omap_sr_autocomp_store(void *data, u64 val)
struct omap_sr *sr_info = data;
if (!sr_info) {
- pr_warning("%s: omap_sr struct not found\n", __func__);
+ pr_warn("%s: omap_sr struct not found\n", __func__);
return -EINVAL;
}
/* Sanity check */
if (val > 1) {
- pr_warning("%s: Invalid argument %lld\n", __func__, val);
+ pr_warn("%s: Invalid argument %lld\n", __func__, val);
return -EINVAL;
}
@@ -848,19 +847,13 @@ static int __init omap_sr_probe(struct platform_device *pdev)
int i, ret = 0;
sr_info = devm_kzalloc(&pdev->dev, sizeof(struct omap_sr), GFP_KERNEL);
- if (!sr_info) {
- dev_err(&pdev->dev, "%s: unable to allocate sr_info\n",
- __func__);
+ if (!sr_info)
return -ENOMEM;
- }
sr_info->name = devm_kzalloc(&pdev->dev,
SMARTREFLEX_NAME_LEN, GFP_KERNEL);
- if (!sr_info->name) {
- dev_err(&pdev->dev, "%s: unable to allocate SR instance name\n",
- __func__);
+ if (!sr_info->name)
return -ENOMEM;
- }
platform_set_drvdata(pdev, sr_info);
@@ -912,7 +905,7 @@ static int __init omap_sr_probe(struct platform_device *pdev)
if (sr_class) {
ret = sr_late_init(sr_info);
if (ret) {
- pr_warning("%s: Error in SR late init\n", __func__);
+ pr_warn("%s: Error in SR late init\n", __func__);
goto err_list_del;
}
}
@@ -923,7 +916,7 @@ static int __init omap_sr_probe(struct platform_device *pdev)
if (IS_ERR_OR_NULL(sr_dbg_dir)) {
ret = PTR_ERR(sr_dbg_dir);
pr_err("%s:sr debugfs dir creation failed(%d)\n",
- __func__, ret);
+ __func__, ret);
goto err_list_del;
}
}
@@ -945,8 +938,8 @@ static int __init omap_sr_probe(struct platform_device *pdev)
nvalue_dir = debugfs_create_dir("nvalue", sr_info->dbg_dir);
if (IS_ERR_OR_NULL(nvalue_dir)) {
- dev_err(&pdev->dev, "%s: Unable to create debugfs directory"
- "for n-values\n", __func__);
+ dev_err(&pdev->dev, "%s: Unable to create debugfs directory for n-values\n",
+ __func__);
ret = PTR_ERR(nvalue_dir);
goto err_debugfs;
}
@@ -1053,12 +1046,12 @@ static int __init sr_init(void)
if (sr_pmic_data && sr_pmic_data->sr_pmic_init)
sr_pmic_data->sr_pmic_init();
else
- pr_warning("%s: No PMIC hook to init smartreflex\n", __func__);
+ pr_warn("%s: No PMIC hook to init smartreflex\n", __func__);
ret = platform_driver_probe(&smartreflex_driver, omap_sr_probe);
if (ret) {
pr_err("%s: platform driver register failed for SR\n",
- __func__);
+ __func__);
return ret;
}
diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c
index fbab29d..243b233 100644
--- a/drivers/powercap/intel_rapl.c
+++ b/drivers/powercap/intel_rapl.c
@@ -1154,8 +1154,8 @@ static const struct x86_cpu_id rapl_ids[] __initconst = {
RAPL_CPU(INTEL_FAM6_ATOM_SILVERMONT1, rapl_defaults_byt),
RAPL_CPU(INTEL_FAM6_ATOM_AIRMONT, rapl_defaults_cht),
- RAPL_CPU(INTEL_FAM6_ATOM_MERRIFIELD1, rapl_defaults_tng),
- RAPL_CPU(INTEL_FAM6_ATOM_MERRIFIELD2, rapl_defaults_ann),
+ RAPL_CPU(INTEL_FAM6_ATOM_MERRIFIELD, rapl_defaults_tng),
+ RAPL_CPU(INTEL_FAM6_ATOM_MOOREFIELD, rapl_defaults_ann),
RAPL_CPU(INTEL_FAM6_ATOM_GOLDMONT, rapl_defaults_core),
RAPL_CPU(INTEL_FAM6_ATOM_DENVERTON, rapl_defaults_core),
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6c88e31..4c2631a 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -353,6 +353,14 @@ config REGULATOR_LTC3589
This enables support for the LTC3589, LTC3589-1, and LTC3589-2
8-output regulators controlled via I2C.
+config REGULATOR_LTC3676
+ tristate "LTC3676 8-output voltage regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This enables support for the LTC3676
+ 8-output regulators controlled via I2C.
+
config REGULATOR_MAX14577
tristate "Maxim 14577/77836 regulator"
depends on MFD_MAX14577
@@ -820,7 +828,7 @@ config REGULATOR_TPS65912
This driver supports TPS65912 voltage regulator chip.
config REGULATOR_TPS80031
- tristate "TI TPS80031/TPS80032 power regualtor driver"
+ tristate "TI TPS80031/TPS80032 power regulator driver"
depends on MFD_TPS80031
help
TPS80031/ TPS80032 Fully Integrated Power Management with Power
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index f3da9ee..2142a5d 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_REGULATOR_LP8788) += lp8788-buck.o
obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
+obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o
obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index db320e8..67426c0 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -679,24 +679,6 @@ static int drms_uA_update(struct regulator_dev *rdev)
!rdev->desc->ops->set_load)
return -EINVAL;
- /* get output voltage */
- output_uV = _regulator_get_voltage(rdev);
- if (output_uV <= 0) {
- rdev_err(rdev, "invalid output voltage found\n");
- return -EINVAL;
- }
-
- /* get input voltage */
- input_uV = 0;
- if (rdev->supply)
- input_uV = regulator_get_voltage(rdev->supply);
- if (input_uV <= 0)
- input_uV = rdev->constraints->input_uV;
- if (input_uV <= 0) {
- rdev_err(rdev, "invalid input voltage found\n");
- return -EINVAL;
- }
-
/* calc total requested load */
list_for_each_entry(sibling, &rdev->consumer_list, list)
current_uA += sibling->uA_load;
@@ -709,6 +691,24 @@ static int drms_uA_update(struct regulator_dev *rdev)
if (err < 0)
rdev_err(rdev, "failed to set load %d\n", current_uA);
} else {
+ /* get output voltage */
+ output_uV = _regulator_get_voltage(rdev);
+ if (output_uV <= 0) {
+ rdev_err(rdev, "invalid output voltage found\n");
+ return -EINVAL;
+ }
+
+ /* get input voltage */
+ input_uV = 0;
+ if (rdev->supply)
+ input_uV = regulator_get_voltage(rdev->supply);
+ if (input_uV <= 0)
+ input_uV = rdev->constraints->input_uV;
+ if (input_uV <= 0) {
+ rdev_err(rdev, "invalid input voltage found\n");
+ return -EINVAL;
+ }
+
/* now get the optimum mode for our new total regulator load */
mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV,
output_uV, current_uA);
@@ -2743,6 +2743,24 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev,
return ret;
}
+static int _regulator_set_voltage_time(struct regulator_dev *rdev,
+ int old_uV, int new_uV)
+{
+ unsigned int ramp_delay = 0;
+
+ if (rdev->constraints->ramp_delay)
+ ramp_delay = rdev->constraints->ramp_delay;
+ else if (rdev->desc->ramp_delay)
+ ramp_delay = rdev->desc->ramp_delay;
+
+ if (ramp_delay == 0) {
+ rdev_warn(rdev, "ramp_delay not set\n");
+ return 0;
+ }
+
+ return DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay);
+}
+
static int _regulator_do_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
@@ -2751,6 +2769,8 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
int best_val = 0;
unsigned int selector;
int old_selector = -1;
+ const struct regulator_ops *ops = rdev->desc->ops;
+ int old_uV = _regulator_get_voltage(rdev);
trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV);
@@ -2762,29 +2782,28 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
* info to call set_voltage_time_sel().
*/
if (_regulator_is_enabled(rdev) &&
- rdev->desc->ops->set_voltage_time_sel &&
- rdev->desc->ops->get_voltage_sel) {
- old_selector = rdev->desc->ops->get_voltage_sel(rdev);
+ ops->set_voltage_time_sel && ops->get_voltage_sel) {
+ old_selector = ops->get_voltage_sel(rdev);
if (old_selector < 0)
return old_selector;
}
- if (rdev->desc->ops->set_voltage) {
+ if (ops->set_voltage) {
ret = _regulator_call_set_voltage(rdev, min_uV, max_uV,
&selector);
if (ret >= 0) {
- if (rdev->desc->ops->list_voltage)
- best_val = rdev->desc->ops->list_voltage(rdev,
- selector);
+ if (ops->list_voltage)
+ best_val = ops->list_voltage(rdev,
+ selector);
else
best_val = _regulator_get_voltage(rdev);
}
- } else if (rdev->desc->ops->set_voltage_sel) {
+ } else if (ops->set_voltage_sel) {
ret = regulator_map_voltage(rdev, min_uV, max_uV);
if (ret >= 0) {
- best_val = rdev->desc->ops->list_voltage(rdev, ret);
+ best_val = ops->list_voltage(rdev, ret);
if (min_uV <= best_val && max_uV >= best_val) {
selector = ret;
if (old_selector == selector)
@@ -2800,34 +2819,50 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
ret = -EINVAL;
}
- /* Call set_voltage_time_sel if successfully obtained old_selector */
- if (ret == 0 && !rdev->constraints->ramp_disable && old_selector >= 0
- && old_selector != selector) {
+ if (ret)
+ goto out;
- delay = rdev->desc->ops->set_voltage_time_sel(rdev,
- old_selector, selector);
- if (delay < 0) {
- rdev_warn(rdev, "set_voltage_time_sel() failed: %d\n",
- delay);
- delay = 0;
+ if (ops->set_voltage_time_sel) {
+ /*
+ * Call set_voltage_time_sel if successfully obtained
+ * old_selector
+ */
+ if (old_selector >= 0 && old_selector != selector)
+ delay = ops->set_voltage_time_sel(rdev, old_selector,
+ selector);
+ } else {
+ if (old_uV != best_val) {
+ if (ops->set_voltage_time)
+ delay = ops->set_voltage_time(rdev, old_uV,
+ best_val);
+ else
+ delay = _regulator_set_voltage_time(rdev,
+ old_uV,
+ best_val);
}
+ }
- /* Insert any necessary delays */
- if (delay >= 1000) {
- mdelay(delay / 1000);
- udelay(delay % 1000);
- } else if (delay) {
- udelay(delay);
- }
+ if (delay < 0) {
+ rdev_warn(rdev, "failed to get delay: %d\n", delay);
+ delay = 0;
}
- if (ret == 0 && best_val >= 0) {
+ /* Insert any necessary delays */
+ if (delay >= 1000) {
+ mdelay(delay / 1000);
+ udelay(delay % 1000);
+ } else if (delay) {
+ udelay(delay);
+ }
+
+ if (best_val >= 0) {
unsigned long data = best_val;
_notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE,
(void *)data);
}
+out:
trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val);
return ret;
@@ -2998,9 +3033,13 @@ int regulator_set_voltage_time(struct regulator *regulator,
int voltage;
int i;
+ if (ops->set_voltage_time)
+ return ops->set_voltage_time(rdev, old_uV, new_uV);
+ else if (!ops->set_voltage_time_sel)
+ return _regulator_set_voltage_time(rdev, old_uV, new_uV);
+
/* Currently requires operations to do this */
- if (!ops->list_voltage || !ops->set_voltage_time_sel
- || !rdev->desc->n_voltages)
+ if (!ops->list_voltage || !rdev->desc->n_voltages)
return -EINVAL;
for (i = 0; i < rdev->desc->n_voltages; i++) {
@@ -3039,19 +3078,8 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int old_selector,
unsigned int new_selector)
{
- unsigned int ramp_delay = 0;
int old_volt, new_volt;
- if (rdev->constraints->ramp_delay)
- ramp_delay = rdev->constraints->ramp_delay;
- else if (rdev->desc->ramp_delay)
- ramp_delay = rdev->desc->ramp_delay;
-
- if (ramp_delay == 0) {
- rdev_warn(rdev, "ramp_delay not set\n");
- return 0;
- }
-
/* sanity check */
if (!rdev->desc->ops->list_voltage)
return -EINVAL;
@@ -3059,7 +3087,11 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
old_volt = rdev->desc->ops->list_voltage(rdev, old_selector);
new_volt = rdev->desc->ops->list_voltage(rdev, new_selector);
- return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay);
+ if (rdev->desc->ops->set_voltage_time)
+ return rdev->desc->ops->set_voltage_time(rdev, old_volt,
+ new_volt);
+ else
+ return _regulator_set_voltage_time(rdev, old_volt, new_volt);
}
EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel);
@@ -3483,10 +3515,8 @@ int regulator_bulk_get(struct device *dev, int num_consumers,
consumers[i].consumer = NULL;
for (i = 0; i < num_consumers; i++) {
- consumers[i].consumer = _regulator_get(dev,
- consumers[i].supply,
- false,
- !consumers[i].optional);
+ consumers[i].consumer = regulator_get(dev,
+ consumers[i].supply);
if (IS_ERR(consumers[i].consumer)) {
ret = PTR_ERR(consumers[i].consumer);
dev_err(dev, "Failed to get supply '%s': %d\n",
diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c
index 3963dfa..8976141 100644
--- a/drivers/regulator/dbx500-prcmu.c
+++ b/drivers/regulator/dbx500-prcmu.c
@@ -75,24 +75,6 @@ static struct ux500_regulator_debug {
u8 *state_after_suspend;
} rdebug;
-void ux500_regulator_suspend_debug(void)
-{
- int i;
-
- for (i = 0; i < rdebug.num_regulators; i++)
- rdebug.state_before_suspend[i] =
- rdebug.regulator_array[i].is_enabled;
-}
-
-void ux500_regulator_resume_debug(void)
-{
- int i;
-
- for (i = 0; i < rdebug.num_regulators; i++)
- rdebug.state_after_suspend[i] =
- rdebug.regulator_array[i].is_enabled;
-}
-
static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p)
{
/* print power state count */
diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c
index 6ad8ab4..6ec1d40 100644
--- a/drivers/regulator/devres.c
+++ b/drivers/regulator/devres.c
@@ -164,11 +164,8 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers,
consumers[i].consumer = NULL;
for (i = 0; i < num_consumers; i++) {
- consumers[i].consumer = _devm_regulator_get(dev,
- consumers[i].supply,
- consumers[i].optional ?
- OPTIONAL_GET :
- NORMAL_GET);
+ consumers[i].consumer = devm_regulator_get(dev,
+ consumers[i].supply);
if (IS_ERR(consumers[i].consumer)) {
ret = PTR_ERR(consumers[i].consumer);
dev_err(dev, "Failed to get supply '%s': %d\n",
diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c
index 42dc5fb..62c5f54 100644
--- a/drivers/regulator/hi6421-regulator.c
+++ b/drivers/regulator/hi6421-regulator.c
@@ -477,7 +477,8 @@ static int hi6421_regulator_buck_set_mode(struct regulator_dev *rdev,
return 0;
}
-unsigned int hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev,
+static unsigned int
+hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev,
int input_uV, int output_uV, int load_uA)
{
struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c
new file mode 100644
index 0000000..e2b476c
--- /dev/null
+++ b/drivers/regulator/ltc3676.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2016 Gateworks Corporation, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#define DRIVER_NAME "ltc3676"
+
+/* LTC3676 Registers */
+#define LTC3676_BUCK1 0x01
+#define LTC3676_BUCK2 0x02
+#define LTC3676_BUCK3 0x03
+#define LTC3676_BUCK4 0x04
+#define LTC3676_LDOA 0x05
+#define LTC3676_LDOB 0x06
+#define LTC3676_SQD1 0x07
+#define LTC3676_SQD2 0x08
+#define LTC3676_CNTRL 0x09
+#define LTC3676_DVB1A 0x0A
+#define LTC3676_DVB1B 0x0B
+#define LTC3676_DVB2A 0x0C
+#define LTC3676_DVB2B 0x0D
+#define LTC3676_DVB3A 0x0E
+#define LTC3676_DVB3B 0x0F
+#define LTC3676_DVB4A 0x10
+#define LTC3676_DVB4B 0x11
+#define LTC3676_MSKIRQ 0x12
+#define LTC3676_MSKPG 0x13
+#define LTC3676_USER 0x14
+#define LTC3676_IRQSTAT 0x15
+#define LTC3676_PGSTATL 0x16
+#define LTC3676_PGSTATRT 0x17
+#define LTC3676_HRST 0x1E
+#define LTC3676_CLIRQ 0x1F
+
+#define LTC3676_DVBxA_REF_SELECT BIT(5)
+
+#define LTC3676_IRQSTAT_PGOOD_TIMEOUT BIT(3)
+#define LTC3676_IRQSTAT_UNDERVOLT_WARN BIT(4)
+#define LTC3676_IRQSTAT_UNDERVOLT_FAULT BIT(5)
+#define LTC3676_IRQSTAT_THERMAL_WARN BIT(6)
+#define LTC3676_IRQSTAT_THERMAL_FAULT BIT(7)
+
+enum ltc3676_reg {
+ LTC3676_SW1,
+ LTC3676_SW2,
+ LTC3676_SW3,
+ LTC3676_SW4,
+ LTC3676_LDO1,
+ LTC3676_LDO2,
+ LTC3676_LDO3,
+ LTC3676_LDO4,
+ LTC3676_NUM_REGULATORS,
+};
+
+struct ltc3676 {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_desc regulator_descs[LTC3676_NUM_REGULATORS];
+ struct regulator_dev *regulators[LTC3676_NUM_REGULATORS];
+};
+
+static int ltc3676_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct ltc3676 *ltc3676 = rdev_get_drvdata(rdev);
+ struct device *dev = ltc3676->dev;
+ int dcdc = rdev_get_id(rdev);
+ int sel;
+
+ dev_dbg(dev, "%s id=%d uV=%d\n", __func__, dcdc, uV);
+ sel = regulator_map_voltage_linear(rdev, uV, uV);
+ if (sel < 0)
+ return sel;
+
+ /* DVBB register follows right after the corresponding DVBA register */
+ return regmap_update_bits(ltc3676->regmap, rdev->desc->vsel_reg + 1,
+ rdev->desc->vsel_mask, sel);
+}
+
+static int ltc3676_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct ltc3676 *ltc3676= rdev_get_drvdata(rdev);
+ struct device *dev = ltc3676->dev;
+ int mask, val;
+ int dcdc = rdev_get_id(rdev);
+
+ dev_dbg(dev, "%s id=%d mode=%d\n", __func__, dcdc, mode);
+
+ mask = LTC3676_DVBxA_REF_SELECT;
+ switch (mode) {
+ case REGULATOR_MODE_STANDBY:
+ val = 0; /* select DVBxA */
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = LTC3676_DVBxA_REF_SELECT; /* select DVBxB */
+ break;
+ default:
+ dev_warn(&rdev->dev, "%s: regulator mode: 0x%x not supported\n",
+ rdev->desc->name, mode);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(ltc3676->regmap, rdev->desc->vsel_reg,
+ mask, val);
+}
+
+static inline unsigned int ltc3676_scale(unsigned int uV, u32 r1, u32 r2)
+{
+ uint64_t tmp;
+ if (uV == 0)
+ return 0;
+ tmp = (uint64_t)uV * r1;
+ do_div(tmp, r2);
+ return uV + (unsigned int)tmp;
+}
+
+static int ltc3676_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct ltc3676 *ltc3676 = config->driver_data;
+ struct regulator_desc *rdesc = &ltc3676->regulator_descs[desc->id];
+ u32 r[2];
+ int ret;
+
+ /* LDO3 has a fixed output */
+ if (desc->id == LTC3676_LDO3)
+ return 0;
+
+ ret = of_property_read_u32_array(np, "lltc,fb-voltage-divider", r, 2);
+ if (ret) {
+ dev_err(ltc3676->dev, "Failed to parse voltage divider: %d\n",
+ ret);
+ return ret;
+ }
+
+ rdesc->min_uV = ltc3676_scale(desc->min_uV, r[0], r[1]);
+ rdesc->uV_step = ltc3676_scale(desc->uV_step, r[0], r[1]);
+ rdesc->fixed_uV = ltc3676_scale(desc->fixed_uV, r[0], r[1]);
+
+ return 0;
+}
+
+/* SW1, SW2, SW3, SW4 linear 0.8V-3.3V with scalar via R1/R2 feeback res */
+static struct regulator_ops ltc3676_linear_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_suspend_voltage = ltc3676_set_suspend_voltage,
+ .set_suspend_mode = ltc3676_set_suspend_mode,
+};
+
+/* LDO1 always on fixed 0.8V-3.3V via scalar via R1/R2 feeback res */
+static struct regulator_ops ltc3676_fixed_standby_regulator_ops = {
+};
+
+/* LDO2, LDO3 fixed (LDO2 has external scalar via R1/R2 feedback res) */
+static struct regulator_ops ltc3676_fixed_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+#define LTC3676_REG(_id, _name, _ops, en_reg, en_bit, dvba_reg, dvb_mask) \
+ [LTC3676_ ## _id] = { \
+ .name = #_name, \
+ .of_match = of_match_ptr(#_name), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .of_parse_cb = ltc3676_of_parse_cb, \
+ .n_voltages = (dvb_mask) + 1, \
+ .min_uV = (dvba_reg) ? 412500 : 0, \
+ .uV_step = (dvba_reg) ? 12500 : 0, \
+ .ramp_delay = (dvba_reg) ? 800 : 0, \
+ .fixed_uV = (dvb_mask) ? 0 : 725000, \
+ .ops = &ltc3676_ ## _ops ## _regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = LTC3676_ ## _id, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = (dvba_reg), \
+ .vsel_mask = (dvb_mask), \
+ .enable_reg = (en_reg), \
+ .enable_mask = (1 << en_bit), \
+ }
+
+#define LTC3676_LINEAR_REG(_id, _name, _en, _dvba) \
+ LTC3676_REG(_id, _name, linear, \
+ LTC3676_ ## _en, 7, \
+ LTC3676_ ## _dvba, 0x1f)
+
+#define LTC3676_FIXED_REG(_id, _name, _en_reg, _en_bit) \
+ LTC3676_REG(_id, _name, fixed, LTC3676_ ## _en_reg, _en_bit, 0, 0)
+
+static struct regulator_desc ltc3676_regulators[LTC3676_NUM_REGULATORS] = {
+ LTC3676_LINEAR_REG(SW1, sw1, BUCK1, DVB1A),
+ LTC3676_LINEAR_REG(SW2, sw2, BUCK2, DVB2A),
+ LTC3676_LINEAR_REG(SW3, sw3, BUCK3, DVB3A),
+ LTC3676_LINEAR_REG(SW4, sw4, BUCK4, DVB4A),
+ LTC3676_REG(LDO1, ldo1, fixed_standby, 0, 0, 0, 0),
+ LTC3676_FIXED_REG(LDO2, ldo2, LDOA, 2),
+ LTC3676_FIXED_REG(LDO3, ldo3, LDOA, 5),
+ LTC3676_FIXED_REG(LDO4, ldo4, LDOB, 2),
+};
+
+static bool ltc3676_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3676_IRQSTAT:
+ case LTC3676_BUCK1:
+ case LTC3676_BUCK2:
+ case LTC3676_BUCK3:
+ case LTC3676_BUCK4:
+ case LTC3676_LDOA:
+ case LTC3676_LDOB:
+ case LTC3676_SQD1:
+ case LTC3676_SQD2:
+ case LTC3676_CNTRL:
+ case LTC3676_DVB1A:
+ case LTC3676_DVB1B:
+ case LTC3676_DVB2A:
+ case LTC3676_DVB2B:
+ case LTC3676_DVB3A:
+ case LTC3676_DVB3B:
+ case LTC3676_DVB4A:
+ case LTC3676_DVB4B:
+ case LTC3676_MSKIRQ:
+ case LTC3676_MSKPG:
+ case LTC3676_USER:
+ case LTC3676_HRST:
+ case LTC3676_CLIRQ:
+ return true;
+ }
+ return false;
+}
+
+static bool ltc3676_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3676_IRQSTAT:
+ case LTC3676_BUCK1:
+ case LTC3676_BUCK2:
+ case LTC3676_BUCK3:
+ case LTC3676_BUCK4:
+ case LTC3676_LDOA:
+ case LTC3676_LDOB:
+ case LTC3676_SQD1:
+ case LTC3676_SQD2:
+ case LTC3676_CNTRL:
+ case LTC3676_DVB1A:
+ case LTC3676_DVB1B:
+ case LTC3676_DVB2A:
+ case LTC3676_DVB2B:
+ case LTC3676_DVB3A:
+ case LTC3676_DVB3B:
+ case LTC3676_DVB4A:
+ case LTC3676_DVB4B:
+ case LTC3676_MSKIRQ:
+ case LTC3676_MSKPG:
+ case LTC3676_USER:
+ case LTC3676_HRST:
+ case LTC3676_CLIRQ:
+ return true;
+ }
+ return false;
+}
+
+static bool ltc3676_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTC3676_IRQSTAT:
+ case LTC3676_PGSTATL:
+ case LTC3676_PGSTATRT:
+ return true;
+ }
+ return false;
+}
+
+static const struct regmap_config ltc3676_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = ltc3676_writeable_reg,
+ .readable_reg = ltc3676_readable_reg,
+ .volatile_reg = ltc3676_volatile_reg,
+ .max_register = LTC3676_CLIRQ,
+ .use_single_rw = true,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t ltc3676_isr(int irq, void *dev_id)
+{
+ struct ltc3676 *ltc3676 = dev_id;
+ struct device *dev = ltc3676->dev;
+ unsigned int i, irqstat, event;
+
+ regmap_read(ltc3676->regmap, LTC3676_IRQSTAT, &irqstat);
+
+ dev_dbg(dev, "irq%d irqstat=0x%02x\n", irq, irqstat);
+ if (irqstat & LTC3676_IRQSTAT_THERMAL_WARN) {
+ dev_warn(dev, "Over-temperature Warning\n");
+ event = REGULATOR_EVENT_OVER_TEMP;
+ for (i = 0; i < LTC3676_NUM_REGULATORS; i++)
+ regulator_notifier_call_chain(ltc3676->regulators[i],
+ event, NULL);
+ }
+
+ if (irqstat & LTC3676_IRQSTAT_UNDERVOLT_WARN) {
+ dev_info(dev, "Undervoltage Warning\n");
+ event = REGULATOR_EVENT_UNDER_VOLTAGE;
+ for (i = 0; i < LTC3676_NUM_REGULATORS; i++)
+ regulator_notifier_call_chain(ltc3676->regulators[i],
+ event, NULL);
+ }
+
+ /* Clear warning condition */
+ regmap_write(ltc3676->regmap, LTC3676_CLIRQ, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int ltc3676_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct regulator_init_data *init_data = dev_get_platdata(dev);
+ struct regulator_desc *descs;
+ struct ltc3676 *ltc3676;
+ int i, ret;
+
+ ltc3676 = devm_kzalloc(dev, sizeof(*ltc3676), GFP_KERNEL);
+ if (!ltc3676)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ltc3676);
+ ltc3676->dev = dev;
+
+ descs = ltc3676->regulator_descs;
+ memcpy(descs, ltc3676_regulators, sizeof(ltc3676_regulators));
+ descs[LTC3676_LDO3].fixed_uV = 1800000; /* LDO3 is fixed 1.8V */
+
+ ltc3676->regmap = devm_regmap_init_i2c(client, &ltc3676_regmap_config);
+ if (IS_ERR(ltc3676->regmap)) {
+ ret = PTR_ERR(ltc3676->regmap);
+ dev_err(dev, "failed to initialize regmap: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < LTC3676_NUM_REGULATORS; i++) {
+ struct regulator_desc *desc = &ltc3676->regulator_descs[i];
+ struct regulator_config config = { };
+
+ if (init_data)
+ config.init_data = &init_data[i];
+
+ config.dev = dev;
+ config.driver_data = ltc3676;
+
+ ltc3676->regulators[i] = devm_regulator_register(dev, desc,
+ &config);
+ if (IS_ERR(ltc3676->regulators[i])) {
+ ret = PTR_ERR(ltc3676->regulators[i]);
+ dev_err(dev, "failed to register regulator %s: %d\n",
+ desc->name, ret);
+ return ret;
+ }
+ }
+
+ regmap_write(ltc3676->regmap, LTC3676_CLIRQ, 0);
+ if (client->irq) {
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ ltc3676_isr,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, ltc3676);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id ltc3676_i2c_id[] = {
+ { "ltc3676" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc3676_i2c_id);
+
+static struct i2c_driver ltc3676_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = ltc3676_regulator_probe,
+ .id_table = ltc3676_i2c_id,
+};
+module_i2c_driver(ltc3676_driver);
+
+MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
+MODULE_DESCRIPTION("Regulator driver for Linear Technology LTC1376");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/pv88080-regulator.c b/drivers/regulator/pv88080-regulator.c
index 81950bd..954a20e 100644
--- a/drivers/regulator/pv88080-regulator.c
+++ b/drivers/regulator/pv88080-regulator.c
@@ -16,6 +16,7 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/regulator/driver.h>
@@ -26,7 +27,7 @@
#include <linux/regulator/of_regulator.h>
#include "pv88080-regulator.h"
-#define PV88080_MAX_REGULATORS 3
+#define PV88080_MAX_REGULATORS 4
/* PV88080 REGULATOR IDs */
enum {
@@ -34,6 +35,12 @@ enum {
PV88080_ID_BUCK1,
PV88080_ID_BUCK2,
PV88080_ID_BUCK3,
+ PV88080_ID_HVBUCK,
+};
+
+enum pv88080_types {
+ TYPE_PV88080_AA,
+ TYPE_PV88080_BA,
};
struct pv88080_regulator {
@@ -42,7 +49,8 @@ struct pv88080_regulator {
unsigned int n_current_limits;
const int *current_limits;
unsigned int limit_mask;
- unsigned int conf;
+ unsigned int mode_reg;
+ unsigned int limit_reg;
unsigned int conf2;
unsigned int conf5;
};
@@ -51,6 +59,8 @@ struct pv88080 {
struct device *dev;
struct regmap *regmap;
struct regulator_dev *rdev[PV88080_MAX_REGULATORS];
+ unsigned long type;
+ const struct pv88080_compatible_regmap *regmap_config;
};
struct pv88080_buck_voltage {
@@ -59,6 +69,30 @@ struct pv88080_buck_voltage {
int uV_step;
};
+struct pv88080_buck_regmap {
+ /* REGS */
+ int buck_enable_reg;
+ int buck_vsel_reg;
+ int buck_mode_reg;
+ int buck_limit_reg;
+ int buck_vdac_range_reg;
+ int buck_vrange_gain_reg;
+ /* MASKS */
+ int buck_enable_mask;
+ int buck_vsel_mask;
+ int buck_limit_mask;
+};
+
+struct pv88080_compatible_regmap {
+ /* BUCK1, 2, 3 */
+ struct pv88080_buck_regmap buck_regmap[PV88080_MAX_REGULATORS-1];
+ /* HVBUCK */
+ int hvbuck_enable_reg;
+ int hvbuck_vsel_reg;
+ int hvbuck_enable_mask;
+ int hvbuck_vsel_mask;
+};
+
static const struct regmap_config pv88080_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -89,13 +123,111 @@ static const struct pv88080_buck_voltage pv88080_buck_vol[2] = {
},
};
+static const struct pv88080_compatible_regmap pv88080_aa_regs = {
+ /* BUCK1 */
+ .buck_regmap[0] = {
+ .buck_enable_reg = PV88080AA_REG_BUCK1_CONF0,
+ .buck_vsel_reg = PV88080AA_REG_BUCK1_CONF0,
+ .buck_mode_reg = PV88080AA_REG_BUCK1_CONF1,
+ .buck_limit_reg = PV88080AA_REG_BUCK1_CONF1,
+ .buck_vdac_range_reg = PV88080AA_REG_BUCK1_CONF2,
+ .buck_vrange_gain_reg = PV88080AA_REG_BUCK1_CONF5,
+ .buck_enable_mask = PV88080_BUCK1_EN,
+ .buck_vsel_mask = PV88080_VBUCK1_MASK,
+ .buck_limit_mask = PV88080_BUCK1_ILIM_MASK,
+ },
+ /* BUCK2 */
+ .buck_regmap[1] = {
+ .buck_enable_reg = PV88080AA_REG_BUCK2_CONF0,
+ .buck_vsel_reg = PV88080AA_REG_BUCK2_CONF0,
+ .buck_mode_reg = PV88080AA_REG_BUCK2_CONF1,
+ .buck_limit_reg = PV88080AA_REG_BUCK2_CONF1,
+ .buck_vdac_range_reg = PV88080AA_REG_BUCK2_CONF2,
+ .buck_vrange_gain_reg = PV88080AA_REG_BUCK2_CONF5,
+ .buck_enable_mask = PV88080_BUCK2_EN,
+ .buck_vsel_mask = PV88080_VBUCK2_MASK,
+ .buck_limit_mask = PV88080_BUCK2_ILIM_MASK,
+ },
+ /* BUCK3 */
+ .buck_regmap[2] = {
+ .buck_enable_reg = PV88080AA_REG_BUCK3_CONF0,
+ .buck_vsel_reg = PV88080AA_REG_BUCK3_CONF0,
+ .buck_mode_reg = PV88080AA_REG_BUCK3_CONF1,
+ .buck_limit_reg = PV88080AA_REG_BUCK3_CONF1,
+ .buck_vdac_range_reg = PV88080AA_REG_BUCK3_CONF2,
+ .buck_vrange_gain_reg = PV88080AA_REG_BUCK3_CONF5,
+ .buck_enable_mask = PV88080_BUCK3_EN,
+ .buck_vsel_mask = PV88080_VBUCK3_MASK,
+ .buck_limit_mask = PV88080_BUCK3_ILIM_MASK,
+ },
+ /* HVBUCK */
+ .hvbuck_enable_reg = PV88080AA_REG_HVBUCK_CONF2,
+ .hvbuck_vsel_reg = PV88080AA_REG_HVBUCK_CONF1,
+ .hvbuck_enable_mask = PV88080_HVBUCK_EN,
+ .hvbuck_vsel_mask = PV88080_VHVBUCK_MASK,
+};
+
+static const struct pv88080_compatible_regmap pv88080_ba_regs = {
+ /* BUCK1 */
+ .buck_regmap[0] = {
+ .buck_enable_reg = PV88080BA_REG_BUCK1_CONF0,
+ .buck_vsel_reg = PV88080BA_REG_BUCK1_CONF0,
+ .buck_mode_reg = PV88080BA_REG_BUCK1_CONF1,
+ .buck_limit_reg = PV88080BA_REG_BUCK1_CONF1,
+ .buck_vdac_range_reg = PV88080BA_REG_BUCK1_CONF2,
+ .buck_vrange_gain_reg = PV88080BA_REG_BUCK1_CONF5,
+ .buck_enable_mask = PV88080_BUCK1_EN,
+ .buck_vsel_mask = PV88080_VBUCK1_MASK,
+ .buck_limit_mask = PV88080_BUCK1_ILIM_MASK,
+ },
+ /* BUCK2 */
+ .buck_regmap[1] = {
+ .buck_enable_reg = PV88080BA_REG_BUCK2_CONF0,
+ .buck_vsel_reg = PV88080BA_REG_BUCK2_CONF0,
+ .buck_mode_reg = PV88080BA_REG_BUCK2_CONF1,
+ .buck_limit_reg = PV88080BA_REG_BUCK2_CONF1,
+ .buck_vdac_range_reg = PV88080BA_REG_BUCK2_CONF2,
+ .buck_vrange_gain_reg = PV88080BA_REG_BUCK2_CONF5,
+ .buck_enable_mask = PV88080_BUCK2_EN,
+ .buck_vsel_mask = PV88080_VBUCK2_MASK,
+ .buck_limit_mask = PV88080_BUCK2_ILIM_MASK,
+ },
+ /* BUCK3 */
+ .buck_regmap[2] = {
+ .buck_enable_reg = PV88080BA_REG_BUCK3_CONF0,
+ .buck_vsel_reg = PV88080BA_REG_BUCK3_CONF0,
+ .buck_mode_reg = PV88080BA_REG_BUCK3_CONF1,
+ .buck_limit_reg = PV88080BA_REG_BUCK3_CONF1,
+ .buck_vdac_range_reg = PV88080BA_REG_BUCK3_CONF2,
+ .buck_vrange_gain_reg = PV88080BA_REG_BUCK3_CONF5,
+ .buck_enable_mask = PV88080_BUCK3_EN,
+ .buck_vsel_mask = PV88080_VBUCK3_MASK,
+ .buck_limit_mask = PV88080_BUCK3_ILIM_MASK,
+ },
+ /* HVBUCK */
+ .hvbuck_enable_reg = PV88080BA_REG_HVBUCK_CONF2,
+ .hvbuck_vsel_reg = PV88080BA_REG_HVBUCK_CONF1,
+ .hvbuck_enable_mask = PV88080_HVBUCK_EN,
+ .hvbuck_vsel_mask = PV88080_VHVBUCK_MASK,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pv88080_dt_ids[] = {
+ { .compatible = "pvs,pv88080", .data = (void *)TYPE_PV88080_AA },
+ { .compatible = "pvs,pv88080-aa", .data = (void *)TYPE_PV88080_AA },
+ { .compatible = "pvs,pv88080-ba", .data = (void *)TYPE_PV88080_BA },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pv88080_dt_ids);
+#endif
+
static unsigned int pv88080_buck_get_mode(struct regulator_dev *rdev)
{
struct pv88080_regulator *info = rdev_get_drvdata(rdev);
unsigned int data;
int ret, mode = 0;
- ret = regmap_read(rdev->regmap, info->conf, &data);
+ ret = regmap_read(rdev->regmap, info->mode_reg, &data);
if (ret < 0)
return ret;
@@ -136,7 +268,7 @@ static int pv88080_buck_set_mode(struct regulator_dev *rdev,
return -EINVAL;
}
- return regmap_update_bits(rdev->regmap, info->conf,
+ return regmap_update_bits(rdev->regmap, info->mode_reg,
PV88080_BUCK1_MODE_MASK, val);
}
@@ -151,7 +283,7 @@ static int pv88080_set_current_limit(struct regulator_dev *rdev, int min,
if (min <= info->current_limits[i]
&& max >= info->current_limits[i]) {
return regmap_update_bits(rdev->regmap,
- info->conf,
+ info->limit_reg,
info->limit_mask,
i << PV88080_BUCK1_ILIM_SHIFT);
}
@@ -166,7 +298,7 @@ static int pv88080_get_current_limit(struct regulator_dev *rdev)
unsigned int data;
int ret;
- ret = regmap_read(rdev->regmap, info->conf, &data);
+ ret = regmap_read(rdev->regmap, info->limit_reg, &data);
if (ret < 0)
return ret;
@@ -187,6 +319,15 @@ static struct regulator_ops pv88080_buck_ops = {
.get_current_limit = pv88080_get_current_limit,
};
+static struct regulator_ops pv88080_hvbuck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
#define PV88080_BUCK(chip, regl_name, min, step, max, limits_array) \
{\
.desc = {\
@@ -200,17 +341,25 @@ static struct regulator_ops pv88080_buck_ops = {
.min_uV = min, \
.uV_step = step, \
.n_voltages = ((max) - (min))/(step) + 1, \
- .enable_reg = PV88080_REG_##regl_name##_CONF0, \
- .enable_mask = PV88080_##regl_name##_EN, \
- .vsel_reg = PV88080_REG_##regl_name##_CONF0, \
- .vsel_mask = PV88080_V##regl_name##_MASK, \
},\
.current_limits = limits_array, \
.n_current_limits = ARRAY_SIZE(limits_array), \
- .limit_mask = PV88080_##regl_name##_ILIM_MASK, \
- .conf = PV88080_REG_##regl_name##_CONF1, \
- .conf2 = PV88080_REG_##regl_name##_CONF2, \
- .conf5 = PV88080_REG_##regl_name##_CONF5, \
+}
+
+#define PV88080_HVBUCK(chip, regl_name, min, step, max) \
+{\
+ .desc = {\
+ .id = chip##_ID_##regl_name,\
+ .name = __stringify(chip##_##regl_name),\
+ .of_match = of_match_ptr(#regl_name),\
+ .regulators_node = of_match_ptr("regulators"),\
+ .type = REGULATOR_VOLTAGE,\
+ .owner = THIS_MODULE,\
+ .ops = &pv88080_hvbuck_ops,\
+ .min_uV = min, \
+ .uV_step = step, \
+ .n_voltages = ((max) - (min))/(step) + 1, \
+ },\
}
static struct pv88080_regulator pv88080_regulator_info[] = {
@@ -220,6 +369,7 @@ static struct pv88080_regulator pv88080_regulator_info[] = {
pv88080_buck23_limits),
PV88080_BUCK(PV88080, BUCK3, 600000, 6250, 1393750,
pv88080_buck23_limits),
+ PV88080_HVBUCK(PV88080, HVBUCK, 0, 5000, 1275000),
};
static irqreturn_t pv88080_irq_handler(int irq, void *data)
@@ -280,6 +430,8 @@ static int pv88080_i2c_probe(struct i2c_client *i2c,
{
struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
struct pv88080 *chip;
+ const struct pv88080_compatible_regmap *regmap_config;
+ const struct of_device_id *match;
struct regulator_config config = { };
int i, error, ret;
unsigned int conf2, conf5;
@@ -297,6 +449,17 @@ static int pv88080_i2c_probe(struct i2c_client *i2c,
return error;
}
+ if (i2c->dev.of_node) {
+ match = of_match_node(pv88080_dt_ids, i2c->dev.of_node);
+ if (!match) {
+ dev_err(chip->dev, "Failed to get of_match_node\n");
+ return -EINVAL;
+ }
+ chip->type = (unsigned long)match->data;
+ } else {
+ chip->type = id->driver_data;
+ }
+
i2c_set_clientdata(i2c, chip);
if (i2c->irq != 0) {
@@ -336,31 +499,58 @@ static int pv88080_i2c_probe(struct i2c_client *i2c,
"Failed to update mask reg: %d\n", ret);
return ret;
}
-
} else {
dev_warn(chip->dev, "No IRQ configured\n");
}
+ switch (chip->type) {
+ case TYPE_PV88080_AA:
+ chip->regmap_config = &pv88080_aa_regs;
+ break;
+ case TYPE_PV88080_BA:
+ chip->regmap_config = &pv88080_ba_regs;
+ break;
+ }
+
+ regmap_config = chip->regmap_config;
config.dev = chip->dev;
config.regmap = chip->regmap;
- for (i = 0; i < PV88080_MAX_REGULATORS; i++) {
+ /* Registeration for BUCK1, 2, 3 */
+ for (i = 0; i < PV88080_MAX_REGULATORS-1; i++) {
if (init_data)
config.init_data = &init_data[i];
+ pv88080_regulator_info[i].limit_reg
+ = regmap_config->buck_regmap[i].buck_limit_reg;
+ pv88080_regulator_info[i].limit_mask
+ = regmap_config->buck_regmap[i].buck_limit_mask;
+ pv88080_regulator_info[i].mode_reg
+ = regmap_config->buck_regmap[i].buck_mode_reg;
+ pv88080_regulator_info[i].conf2
+ = regmap_config->buck_regmap[i].buck_vdac_range_reg;
+ pv88080_regulator_info[i].conf5
+ = regmap_config->buck_regmap[i].buck_vrange_gain_reg;
+ pv88080_regulator_info[i].desc.enable_reg
+ = regmap_config->buck_regmap[i].buck_enable_reg;
+ pv88080_regulator_info[i].desc.enable_mask
+ = regmap_config->buck_regmap[i].buck_enable_mask;
+ pv88080_regulator_info[i].desc.vsel_reg
+ = regmap_config->buck_regmap[i].buck_vsel_reg;
+ pv88080_regulator_info[i].desc.vsel_mask
+ = regmap_config->buck_regmap[i].buck_vsel_mask;
+
ret = regmap_read(chip->regmap,
- pv88080_regulator_info[i].conf2, &conf2);
+ pv88080_regulator_info[i].conf2, &conf2);
if (ret < 0)
return ret;
-
conf2 = ((conf2 >> PV88080_BUCK_VDAC_RANGE_SHIFT) &
PV88080_BUCK_VDAC_RANGE_MASK);
ret = regmap_read(chip->regmap,
- pv88080_regulator_info[i].conf5, &conf5);
+ pv88080_regulator_info[i].conf5, &conf5);
if (ret < 0)
return ret;
-
conf5 = ((conf5 >> PV88080_BUCK_VRANGE_GAIN_SHIFT) &
PV88080_BUCK_VRANGE_GAIN_MASK);
@@ -383,23 +573,38 @@ static int pv88080_i2c_probe(struct i2c_client *i2c,
}
}
+ pv88080_regulator_info[PV88080_ID_HVBUCK].desc.enable_reg
+ = regmap_config->hvbuck_enable_reg;
+ pv88080_regulator_info[PV88080_ID_HVBUCK].desc.enable_mask
+ = regmap_config->hvbuck_enable_mask;
+ pv88080_regulator_info[PV88080_ID_HVBUCK].desc.vsel_reg
+ = regmap_config->hvbuck_vsel_reg;
+ pv88080_regulator_info[PV88080_ID_HVBUCK].desc.vsel_mask
+ = regmap_config->hvbuck_vsel_mask;
+
+ /* Registeration for HVBUCK */
+ if (init_data)
+ config.init_data = &init_data[PV88080_ID_HVBUCK];
+
+ config.driver_data = (void *)&pv88080_regulator_info[PV88080_ID_HVBUCK];
+ chip->rdev[PV88080_ID_HVBUCK] = devm_regulator_register(chip->dev,
+ &pv88080_regulator_info[PV88080_ID_HVBUCK].desc, &config);
+ if (IS_ERR(chip->rdev[PV88080_ID_HVBUCK])) {
+ dev_err(chip->dev, "Failed to register PV88080 regulator\n");
+ return PTR_ERR(chip->rdev[PV88080_ID_HVBUCK]);
+ }
+
return 0;
}
static const struct i2c_device_id pv88080_i2c_id[] = {
- {"pv88080", 0},
+ { "pv88080", TYPE_PV88080_AA },
+ { "pv88080-aa", TYPE_PV88080_AA },
+ { "pv88080-ba", TYPE_PV88080_BA },
{},
};
MODULE_DEVICE_TABLE(i2c, pv88080_i2c_id);
-#ifdef CONFIG_OF
-static const struct of_device_id pv88080_dt_ids[] = {
- { .compatible = "pvs,pv88080", .data = &pv88080_i2c_id[0] },
- {},
-};
-MODULE_DEVICE_TABLE(of, pv88080_dt_ids);
-#endif
-
static struct i2c_driver pv88080_regulator_driver = {
.driver = {
.name = "pv88080",
diff --git a/drivers/regulator/pv88080-regulator.h b/drivers/regulator/pv88080-regulator.h
index 5e9afde..ae25ff3 100644
--- a/drivers/regulator/pv88080-regulator.h
+++ b/drivers/regulator/pv88080-regulator.h
@@ -17,55 +17,75 @@
#define __PV88080_REGISTERS_H__
/* System Control and Event Registers */
-#define PV88080_REG_EVENT_A 0x04
-#define PV88080_REG_MASK_A 0x09
-#define PV88080_REG_MASK_B 0x0a
-#define PV88080_REG_MASK_C 0x0b
-
-/* Regulator Registers */
-#define PV88080_REG_BUCK1_CONF0 0x27
-#define PV88080_REG_BUCK1_CONF1 0x28
-#define PV88080_REG_BUCK1_CONF2 0x59
-#define PV88080_REG_BUCK1_CONF5 0x5c
-#define PV88080_REG_BUCK2_CONF0 0x29
-#define PV88080_REG_BUCK2_CONF1 0x2a
-#define PV88080_REG_BUCK2_CONF2 0x61
-#define PV88080_REG_BUCK2_CONF5 0x64
-#define PV88080_REG_BUCK3_CONF0 0x2b
-#define PV88080_REG_BUCK3_CONF1 0x2c
-#define PV88080_REG_BUCK3_CONF2 0x69
-#define PV88080_REG_BUCK3_CONF5 0x6c
+#define PV88080_REG_EVENT_A 0x04
+#define PV88080_REG_MASK_A 0x09
+#define PV88080_REG_MASK_B 0x0A
+#define PV88080_REG_MASK_C 0x0B
+
+/* Regulator Registers - rev. AA */
+#define PV88080AA_REG_HVBUCK_CONF1 0x2D
+#define PV88080AA_REG_HVBUCK_CONF2 0x2E
+#define PV88080AA_REG_BUCK1_CONF0 0x27
+#define PV88080AA_REG_BUCK1_CONF1 0x28
+#define PV88080AA_REG_BUCK1_CONF2 0x59
+#define PV88080AA_REG_BUCK1_CONF5 0x5C
+#define PV88080AA_REG_BUCK2_CONF0 0x29
+#define PV88080AA_REG_BUCK2_CONF1 0x2A
+#define PV88080AA_REG_BUCK2_CONF2 0x61
+#define PV88080AA_REG_BUCK2_CONF5 0x64
+#define PV88080AA_REG_BUCK3_CONF0 0x2B
+#define PV88080AA_REG_BUCK3_CONF1 0x2C
+#define PV88080AA_REG_BUCK3_CONF2 0x69
+#define PV88080AA_REG_BUCK3_CONF5 0x6C
+
+/* Regulator Registers - rev. BA */
+#define PV88080BA_REG_HVBUCK_CONF1 0x33
+#define PV88080BA_REG_HVBUCK_CONF2 0x34
+#define PV88080BA_REG_BUCK1_CONF0 0x2A
+#define PV88080BA_REG_BUCK1_CONF1 0x2C
+#define PV88080BA_REG_BUCK1_CONF2 0x5A
+#define PV88080BA_REG_BUCK1_CONF5 0x5D
+#define PV88080BA_REG_BUCK2_CONF0 0x2D
+#define PV88080BA_REG_BUCK2_CONF1 0x2F
+#define PV88080BA_REG_BUCK2_CONF2 0x63
+#define PV88080BA_REG_BUCK2_CONF5 0x66
+#define PV88080BA_REG_BUCK3_CONF0 0x30
+#define PV88080BA_REG_BUCK3_CONF1 0x32
+#define PV88080BA_REG_BUCK3_CONF2 0x6C
+#define PV88080BA_REG_BUCK3_CONF5 0x6F
/* PV88080_REG_EVENT_A (addr=0x04) */
#define PV88080_E_VDD_FLT 0x01
-#define PV88080_E_OVER_TEMP 0x02
+#define PV88080_E_OVER_TEMP 0x02
/* PV88080_REG_MASK_A (addr=0x09) */
#define PV88080_M_VDD_FLT 0x01
-#define PV88080_M_OVER_TEMP 0x02
+#define PV88080_M_OVER_TEMP 0x02
-/* PV88080_REG_BUCK1_CONF0 (addr=0x27) */
+/* PV88080_REG_BUCK1_CONF0 (addr=0x27|0x2A) */
#define PV88080_BUCK1_EN 0x80
-#define PV88080_VBUCK1_MASK 0x7F
-/* PV88080_REG_BUCK2_CONF0 (addr=0x29) */
+#define PV88080_VBUCK1_MASK 0x7F
+
+/* PV88080_REG_BUCK2_CONF0 (addr=0x29|0x2D) */
#define PV88080_BUCK2_EN 0x80
-#define PV88080_VBUCK2_MASK 0x7F
-/* PV88080_REG_BUCK3_CONF0 (addr=0x2b) */
+#define PV88080_VBUCK2_MASK 0x7F
+
+/* PV88080_REG_BUCK3_CONF0 (addr=0x2B|0x30) */
#define PV88080_BUCK3_EN 0x80
-#define PV88080_VBUCK3_MASK 0x7F
+#define PV88080_VBUCK3_MASK 0x7F
-/* PV88080_REG_BUCK1_CONF1 (addr=0x28) */
-#define PV88080_BUCK1_ILIM_SHIFT 2
+/* PV88080_REG_BUCK1_CONF1 (addr=0x28|0x2C) */
+#define PV88080_BUCK1_ILIM_SHIFT 2
#define PV88080_BUCK1_ILIM_MASK 0x0C
#define PV88080_BUCK1_MODE_MASK 0x03
-/* PV88080_REG_BUCK2_CONF1 (addr=0x2a) */
-#define PV88080_BUCK2_ILIM_SHIFT 2
+/* PV88080_REG_BUCK2_CONF1 (addr=0x2A|0x2F) */
+#define PV88080_BUCK2_ILIM_SHIFT 2
#define PV88080_BUCK2_ILIM_MASK 0x0C
#define PV88080_BUCK2_MODE_MASK 0x03
-/* PV88080_REG_BUCK3_CONF1 (addr=0x2c) */
-#define PV88080_BUCK3_ILIM_SHIFT 2
+/* PV88080_REG_BUCK3_CONF1 (addr=0x2C|0x32) */
+#define PV88080_BUCK3_ILIM_SHIFT 2
#define PV88080_BUCK3_ILIM_MASK 0x0C
#define PV88080_BUCK3_MODE_MASK 0x03
@@ -73,20 +93,26 @@
#define PV88080_BUCK_MODE_AUTO 0x01
#define PV88080_BUCK_MODE_SYNC 0x02
-/* PV88080_REG_BUCK2_CONF2 (addr=0x61) */
-/* PV88080_REG_BUCK3_CONF2 (addr=0x69) */
-#define PV88080_BUCK_VDAC_RANGE_SHIFT 7
-#define PV88080_BUCK_VDAC_RANGE_MASK 0x01
+/* PV88080_REG_HVBUCK_CONF1 (addr=0x2D|0x33) */
+#define PV88080_VHVBUCK_MASK 0xFF
+
+/* PV88080_REG_HVBUCK_CONF1 (addr=0x2E|0x34) */
+#define PV88080_HVBUCK_EN 0x01
+
+/* PV88080_REG_BUCK2_CONF2 (addr=0x61|0x63) */
+/* PV88080_REG_BUCK3_CONF2 (addr=0x69|0x6C) */
+#define PV88080_BUCK_VDAC_RANGE_SHIFT 7
+#define PV88080_BUCK_VDAC_RANGE_MASK 0x01
-#define PV88080_BUCK_VDAC_RANGE_1 0x00
-#define PV88080_BUCK_VDAC_RANGE_2 0x01
+#define PV88080_BUCK_VDAC_RANGE_1 0x00
+#define PV88080_BUCK_VDAC_RANGE_2 0x01
-/* PV88080_REG_BUCK2_CONF5 (addr=0x64) */
-/* PV88080_REG_BUCK3_CONF5 (addr=0x6c) */
-#define PV88080_BUCK_VRANGE_GAIN_SHIFT 0
-#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01
+/* PV88080_REG_BUCK2_CONF5 (addr=0x64|0x66) */
+/* PV88080_REG_BUCK3_CONF5 (addr=0x6C|0x6F) */
+#define PV88080_BUCK_VRANGE_GAIN_SHIFT 0
+#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01
-#define PV88080_BUCK_VRANGE_GAIN_1 0x00
-#define PV88080_BUCK_VRANGE_GAIN_2 0x01
+#define PV88080_BUCK_VRANGE_GAIN_1 0x00
+#define PV88080_BUCK_VRANGE_GAIN_2 0x01
#endif /* __PV88080_REGISTERS_H__ */
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index c245242..1b88e0e1 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -10,7 +10,6 @@
* published by the Free Software Foundation.
*/
-#include <linux/delay.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
@@ -194,12 +193,10 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle;
unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle;
unsigned int duty_unit = drvdata->continuous.dutycycle_unit;
- unsigned int ramp_delay = rdev->constraints->ramp_delay;
int min_uV = rdev->constraints->min_uV;
int max_uV = rdev->constraints->max_uV;
int diff_uV = max_uV - min_uV;
struct pwm_state pstate;
- int old_uV = pwm_regulator_get_voltage(rdev);
unsigned int diff_duty;
unsigned int dutycycle;
int ret;
@@ -233,13 +230,6 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
return ret;
}
- if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev))
- return 0;
-
- /* Ramp delay is in uV/uS. Adjust to uS and delay */
- ramp_delay = DIV_ROUND_UP(abs(req_min_uV - old_uV), ramp_delay);
- usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10));
-
return 0;
}
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
index 40d07ba..a676749 100644
--- a/drivers/regulator/rk808-regulator.c
+++ b/drivers/regulator/rk808-regulator.c
@@ -533,8 +533,7 @@ static int rk808_regulator_probe(struct platform_device *pdev)
static struct platform_driver rk808_regulator_driver = {
.probe = rk808_regulator_probe,
.driver = {
- .name = "rk808-regulator",
- .owner = THIS_MODULE,
+ .name = "rk808-regulator"
},
};
diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c
index d1e631d..eb0f5b1 100644
--- a/drivers/regulator/tps65218-regulator.c
+++ b/drivers/regulator/tps65218-regulator.c
@@ -180,6 +180,14 @@ static int tps65218_pmic_set_suspend_disable(struct regulator_dev *dev)
if (rid < TPS65218_DCDC_1 || rid > TPS65218_LDO_1)
return -EINVAL;
+ /*
+ * Certain revisions of TPS65218 will need to have DCDC3 regulator
+ * enabled always, otherwise an immediate system reboot will occur
+ * during poweroff.
+ */
+ if (rid == TPS65218_DCDC_3 && tps->rev == TPS65218_REV_2_1)
+ return 0;
+
if (!tps->info[rid]->strobe) {
if (rid == TPS65218_DCDC_3)
tps->info[rid]->strobe = 3;
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index fb991ec..696116e 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -1111,6 +1111,12 @@ static int tps65910_probe(struct platform_device *pdev)
pmic->num_regulators = ARRAY_SIZE(tps65910_regs);
pmic->ext_sleep_control = tps65910_ext_sleep_control;
info = tps65910_regs;
+ /* Work around silicon erratum SWCZ010: output programmed
+ * voltage level can go higher than expected or crash
+ * Workaround: use no synchronization of DCDC clocks
+ */
+ tps65910_reg_clear_bits(pmic->mfd, TPS65910_DCDCCTRL,
+ DCDCCTRL_DCDCCKSYNC_MASK);
break;
case TPS65911:
pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c
index ea607a4..554eaa1 100644
--- a/drivers/s390/char/sclp_ctl.c
+++ b/drivers/s390/char/sclp_ctl.c
@@ -126,21 +126,4 @@ static struct miscdevice sclp_ctl_device = {
.name = "sclp",
.fops = &sclp_ctl_fops,
};
-
-/*
- * Register sclp_ctl misc device
- */
-static int __init sclp_ctl_init(void)
-{
- return misc_register(&sclp_ctl_device);
-}
-module_init(sclp_ctl_init);
-
-/*
- * Deregister sclp_ctl misc device
- */
-static void __exit sclp_ctl_exit(void)
-{
- misc_deregister(&sclp_ctl_device);
-}
-module_exit(sclp_ctl_exit);
+module_misc_device(sclp_ctl_device);
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index ba9af4a..ec6381e 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -486,6 +486,8 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
else
shost->dma_boundary = 0xffffffff;
+ shost->use_blk_mq = scsi_use_blk_mq;
+
device_initialize(&shost->shost_gendev);
dev_set_name(&shost->shost_gendev, "host%d", shost->host_no);
shost->shost_gendev.bus = &scsi_bus_type;
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 1f36aca..1deb6ad 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -1160,7 +1160,6 @@ bool scsi_use_blk_mq = true;
bool scsi_use_blk_mq = false;
#endif
module_param_named(use_blk_mq, scsi_use_blk_mq, bool, S_IWUSR | S_IRUGO);
-EXPORT_SYMBOL_GPL(scsi_use_blk_mq);
static int __init init_scsi(void)
{
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
index 57a4b99..85c8a51 100644
--- a/drivers/scsi/scsi_priv.h
+++ b/drivers/scsi/scsi_priv.h
@@ -29,6 +29,7 @@ extern int scsi_init_hosts(void);
extern void scsi_exit_hosts(void);
/* scsi.c */
+extern bool scsi_use_blk_mq;
extern int scsi_setup_command_freelist(struct Scsi_Host *shost);
extern void scsi_destroy_command_freelist(struct Scsi_Host *shost);
#ifdef CONFIG_SCSI_LOGGING
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index 7dbbb29..deefab3 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -107,8 +107,8 @@ struct virtio_scsi {
/* If the affinity hint is set for virtqueues */
bool affinity_hint_set;
- /* CPU hotplug notifier */
- struct notifier_block nb;
+ struct hlist_node node;
+ struct hlist_node node_dead;
/* Protected by event_vq lock */
bool stop_events;
@@ -118,6 +118,7 @@ struct virtio_scsi {
struct virtio_scsi_vq req_vqs[];
};
+static enum cpuhp_state virtioscsi_online;
static struct kmem_cache *virtscsi_cmd_cache;
static mempool_t *virtscsi_cmd_pool;
@@ -852,21 +853,33 @@ static void virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity)
put_online_cpus();
}
-static int virtscsi_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
+static int virtscsi_cpu_online(unsigned int cpu, struct hlist_node *node)
{
- struct virtio_scsi *vscsi = container_of(nfb, struct virtio_scsi, nb);
- switch(action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- __virtscsi_set_affinity(vscsi, true);
- break;
- default:
- break;
- }
- return NOTIFY_OK;
+ struct virtio_scsi *vscsi = hlist_entry_safe(node, struct virtio_scsi,
+ node);
+ __virtscsi_set_affinity(vscsi, true);
+ return 0;
+}
+
+static int virtscsi_cpu_notif_add(struct virtio_scsi *vi)
+{
+ int ret;
+
+ ret = cpuhp_state_add_instance(virtioscsi_online, &vi->node);
+ if (ret)
+ return ret;
+
+ ret = cpuhp_state_add_instance(CPUHP_VIRT_SCSI_DEAD, &vi->node_dead);
+ if (ret)
+ cpuhp_state_remove_instance(virtioscsi_online, &vi->node);
+ return ret;
+}
+
+static void virtscsi_cpu_notif_remove(struct virtio_scsi *vi)
+{
+ cpuhp_state_remove_instance_nocalls(virtioscsi_online, &vi->node);
+ cpuhp_state_remove_instance_nocalls(CPUHP_VIRT_SCSI_DEAD,
+ &vi->node_dead);
}
static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq,
@@ -929,8 +942,6 @@ static int virtscsi_init(struct virtio_device *vdev,
virtscsi_init_vq(&vscsi->req_vqs[i - VIRTIO_SCSI_VQ_BASE],
vqs[i]);
- virtscsi_set_affinity(vscsi, true);
-
virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE);
@@ -987,12 +998,9 @@ static int virtscsi_probe(struct virtio_device *vdev)
if (err)
goto virtscsi_init_failed;
- vscsi->nb.notifier_call = &virtscsi_cpu_callback;
- err = register_hotcpu_notifier(&vscsi->nb);
- if (err) {
- pr_err("registering cpu notifier failed\n");
+ err = virtscsi_cpu_notif_add(vscsi);
+ if (err)
goto scsi_add_host_failed;
- }
cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1;
shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue);
@@ -1049,7 +1057,7 @@ static void virtscsi_remove(struct virtio_device *vdev)
scsi_remove_host(shost);
- unregister_hotcpu_notifier(&vscsi->nb);
+ virtscsi_cpu_notif_remove(vscsi);
virtscsi_remove_vqs(vdev);
scsi_host_put(shost);
@@ -1061,7 +1069,7 @@ static int virtscsi_freeze(struct virtio_device *vdev)
struct Scsi_Host *sh = virtio_scsi_host(vdev);
struct virtio_scsi *vscsi = shost_priv(sh);
- unregister_hotcpu_notifier(&vscsi->nb);
+ virtscsi_cpu_notif_remove(vscsi);
virtscsi_remove_vqs(vdev);
return 0;
}
@@ -1076,12 +1084,11 @@ static int virtscsi_restore(struct virtio_device *vdev)
if (err)
return err;
- err = register_hotcpu_notifier(&vscsi->nb);
+ err = virtscsi_cpu_notif_add(vscsi);
if (err) {
vdev->config->del_vqs(vdev);
return err;
}
-
virtio_device_ready(vdev);
if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
@@ -1136,6 +1143,16 @@ static int __init init(void)
pr_err("mempool_create() for virtscsi_cmd_pool failed\n");
goto error;
}
+ ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
+ "scsi/virtio:online",
+ virtscsi_cpu_online, NULL);
+ if (ret < 0)
+ goto error;
+ virtioscsi_online = ret;
+ ret = cpuhp_setup_state_multi(CPUHP_VIRT_SCSI_DEAD, "scsi/virtio:dead",
+ NULL, virtscsi_cpu_online);
+ if (ret)
+ goto error;
ret = register_virtio_driver(&virtio_scsi_driver);
if (ret < 0)
goto error;
@@ -1151,12 +1168,17 @@ error:
kmem_cache_destroy(virtscsi_cmd_cache);
virtscsi_cmd_cache = NULL;
}
+ if (virtioscsi_online)
+ cpuhp_remove_multi_state(virtioscsi_online);
+ cpuhp_remove_multi_state(CPUHP_VIRT_SCSI_DEAD);
return ret;
}
static void __exit fini(void)
{
unregister_virtio_driver(&virtio_scsi_driver);
+ cpuhp_remove_multi_state(virtioscsi_online);
+ cpuhp_remove_multi_state(CPUHP_VIRT_SCSI_DEAD);
mempool_destroy(virtscsi_cmd_pool);
kmem_cache_destroy(virtscsi_cmd_cache);
}
diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 4822346..7112004 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -215,29 +215,22 @@ no_clk:
/* Assign the child power domains to their parents */
for_each_matching_node(np, exynos_pm_domain_of_match) {
- struct generic_pm_domain *child_domain, *parent_domain;
- struct of_phandle_args args;
+ struct of_phandle_args child, parent;
- args.np = np;
- args.args_count = 0;
- child_domain = of_genpd_get_from_provider(&args);
- if (IS_ERR(child_domain))
- continue;
+ child.np = np;
+ child.args_count = 0;
if (of_parse_phandle_with_args(np, "power-domains",
- "#power-domain-cells", 0, &args) != 0)
- continue;
-
- parent_domain = of_genpd_get_from_provider(&args);
- if (IS_ERR(parent_domain))
+ "#power-domain-cells", 0,
+ &parent) != 0)
continue;
- if (pm_genpd_add_subdomain(parent_domain, child_domain))
+ if (of_genpd_add_subdomain(&parent, &child))
pr_warn("%s failed to add subdomain: %s\n",
- parent_domain->name, child_domain->name);
+ parent.np->name, child.np->name);
else
pr_info("%s has as child subdomain: %s.\n",
- parent_domain->name, child_domain->name);
+ parent.np->name, child.np->name);
}
return 0;
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index aca282d..5ec3a59 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -954,6 +954,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
if (channel > 5) {
dev_err(&pdev->dev, "invalid channel (%u) specified.\n",
channel);
+ err = -EINVAL;
goto err_put_ctrl;
}
diff --git a/drivers/staging/board/board.c b/drivers/staging/board/board.c
index 45807d8..86dc411 100644
--- a/drivers/staging/board/board.c
+++ b/drivers/staging/board/board.c
@@ -140,7 +140,6 @@ static int board_staging_add_dev_domain(struct platform_device *pdev,
const char *domain)
{
struct of_phandle_args pd_args;
- struct generic_pm_domain *pd;
struct device_node *np;
np = of_find_node_by_path(domain);
@@ -151,14 +150,8 @@ static int board_staging_add_dev_domain(struct platform_device *pdev,
pd_args.np = np;
pd_args.args_count = 0;
- pd = of_genpd_get_from_provider(&pd_args);
- if (IS_ERR(pd)) {
- pr_err("Cannot find genpd %s (%ld)\n", domain, PTR_ERR(pd));
- return PTR_ERR(pd);
- }
- pr_debug("Found genpd %s for device %s\n", pd->name, pdev->name);
- return pm_genpd_add_device(pd, &pdev->dev);
+ return of_genpd_add_device(&pd_args, &pdev->dev);
}
#else
static inline int board_staging_add_dev_domain(struct platform_device *pdev,
diff --git a/drivers/staging/fsl-mc/bus/mc-msi.c b/drivers/staging/fsl-mc/bus/mc-msi.c
index c7be156..4fd8e41 100644
--- a/drivers/staging/fsl-mc/bus/mc-msi.c
+++ b/drivers/staging/fsl-mc/bus/mc-msi.c
@@ -213,7 +213,7 @@ static int fsl_mc_msi_alloc_descs(struct device *dev, unsigned int irq_count)
struct msi_desc *msi_desc;
for (i = 0; i < irq_count; i++) {
- msi_desc = alloc_msi_entry(dev);
+ msi_desc = alloc_msi_entry(dev, 1, NULL);
if (!msi_desc) {
dev_err(dev, "Failed to allocate msi entry\n");
error = -ENOMEM;
@@ -221,7 +221,6 @@ static int fsl_mc_msi_alloc_descs(struct device *dev, unsigned int irq_count)
}
msi_desc->fsl_mc.msi_index = i;
- msi_desc->nvec_used = 1;
INIT_LIST_HEAD(&msi_desc->list);
list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
}
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 51e0d32..a23fa5e 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -800,7 +800,7 @@ out_free_file:
return retval;
}
-static struct file_operations ptmx_fops;
+static struct file_operations ptmx_fops __ro_after_init;
static void __init unix98_pty_init(void)
{
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 1a16fea..a697a85 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -31,6 +31,11 @@ struct uart_8250_dma {
struct dma_chan *rxchan;
struct dma_chan *txchan;
+ /* Device address base for DMA operations */
+ phys_addr_t rx_dma_addr;
+ phys_addr_t tx_dma_addr;
+
+ /* DMA address of the buffer in memory */
dma_addr_t rx_addr;
dma_addr_t tx_addr;
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index dcf43f6..240a361 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -639,7 +639,7 @@ static int univ8250_console_match(struct console *co, char *name, int idx,
{
char match[] = "uart"; /* 8250-specific earlycon name */
unsigned char iotype;
- unsigned long addr;
+ resource_size_t addr;
int i;
if (strncmp(name, match, 4) != 0)
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 3590d01..fdbddbc 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -142,7 +142,7 @@ void serial8250_rx_dma_flush(struct uart_8250_port *p)
if (dma->rx_running) {
dmaengine_pause(dma->rxchan);
__dma_rx_complete(p);
- dmaengine_terminate_all(dma->rxchan);
+ dmaengine_terminate_async(dma->rxchan);
}
}
EXPORT_SYMBOL_GPL(serial8250_rx_dma_flush);
@@ -150,6 +150,10 @@ EXPORT_SYMBOL_GPL(serial8250_rx_dma_flush);
int serial8250_request_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
+ phys_addr_t rx_dma_addr = dma->rx_dma_addr ?
+ dma->rx_dma_addr : p->port.mapbase;
+ phys_addr_t tx_dma_addr = dma->tx_dma_addr ?
+ dma->tx_dma_addr : p->port.mapbase;
dma_cap_mask_t mask;
struct dma_slave_caps caps;
int ret;
@@ -157,11 +161,11 @@ int serial8250_request_dma(struct uart_8250_port *p)
/* Default slave configuration parameters */
dma->rxconf.direction = DMA_DEV_TO_MEM;
dma->rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- dma->rxconf.src_addr = p->port.mapbase + UART_RX;
+ dma->rxconf.src_addr = rx_dma_addr + UART_RX;
dma->txconf.direction = DMA_MEM_TO_DEV;
dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- dma->txconf.dst_addr = p->port.mapbase + UART_TX;
+ dma->txconf.dst_addr = tx_dma_addr + UART_TX;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
@@ -247,14 +251,14 @@ void serial8250_release_dma(struct uart_8250_port *p)
return;
/* Release RX resources */
- dmaengine_terminate_all(dma->rxchan);
+ dmaengine_terminate_sync(dma->rxchan);
dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf,
dma->rx_addr);
dma_release_channel(dma->rxchan);
dma->rxchan = NULL;
/* Release TX resources */
- dmaengine_terminate_all(dma->txchan);
+ dmaengine_terminate_sync(dma->txchan);
dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,
UART_XMIT_SIZE, DMA_TO_DEVICE);
dma_release_channel(dma->txchan);
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index e199696..459d726 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -298,12 +298,17 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
p->serial_out = dw8250_serial_out32be;
}
} else if (has_acpi_companion(p->dev)) {
- p->iotype = UPIO_MEM32;
- p->regshift = 2;
- p->serial_in = dw8250_serial_in32;
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(p->dev->driver->acpi_match_table,
+ p->dev);
+ if (id && !strcmp(id->id, "APMC0D08")) {
+ p->iotype = UPIO_MEM32;
+ p->regshift = 2;
+ p->serial_in = dw8250_serial_in32;
+ data->uart_16550_compatible = true;
+ }
p->set_termios = dw8250_set_termios;
- /* So far none of there implement the Busy Functionality */
- data->uart_16550_compatible = true;
}
/* Platforms with iDMA */
@@ -360,18 +365,19 @@ static int dw8250_probe(struct platform_device *pdev)
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
struct uart_port *p = &uart.port;
+ struct device *dev = &pdev->dev;
struct dw8250_data *data;
int err;
u32 val;
if (!regs) {
- dev_err(&pdev->dev, "no registers defined\n");
+ dev_err(dev, "no registers defined\n");
return -EINVAL;
}
if (irq < 0) {
if (irq != -EPROBE_DEFER)
- dev_err(&pdev->dev, "cannot get irq\n");
+ dev_err(dev, "cannot get irq\n");
return irq;
}
@@ -382,16 +388,16 @@ static int dw8250_probe(struct platform_device *pdev)
p->pm = dw8250_do_pm;
p->type = PORT_8250;
p->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT;
- p->dev = &pdev->dev;
+ p->dev = dev;
p->iotype = UPIO_MEM;
p->serial_in = dw8250_serial_in;
p->serial_out = dw8250_serial_out;
- p->membase = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
+ p->membase = devm_ioremap(dev, regs->start, resource_size(regs));
if (!p->membase)
return -ENOMEM;
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -399,57 +405,57 @@ static int dw8250_probe(struct platform_device *pdev)
data->usr_reg = DW_UART_USR;
p->private_data = data;
- data->uart_16550_compatible = device_property_read_bool(p->dev,
+ data->uart_16550_compatible = device_property_read_bool(dev,
"snps,uart-16550-compatible");
- err = device_property_read_u32(p->dev, "reg-shift", &val);
+ err = device_property_read_u32(dev, "reg-shift", &val);
if (!err)
p->regshift = val;
- err = device_property_read_u32(p->dev, "reg-io-width", &val);
+ err = device_property_read_u32(dev, "reg-io-width", &val);
if (!err && val == 4) {
p->iotype = UPIO_MEM32;
p->serial_in = dw8250_serial_in32;
p->serial_out = dw8250_serial_out32;
}
- if (device_property_read_bool(p->dev, "dcd-override")) {
+ if (device_property_read_bool(dev, "dcd-override")) {
/* Always report DCD as active */
data->msr_mask_on |= UART_MSR_DCD;
data->msr_mask_off |= UART_MSR_DDCD;
}
- if (device_property_read_bool(p->dev, "dsr-override")) {
+ if (device_property_read_bool(dev, "dsr-override")) {
/* Always report DSR as active */
data->msr_mask_on |= UART_MSR_DSR;
data->msr_mask_off |= UART_MSR_DDSR;
}
- if (device_property_read_bool(p->dev, "cts-override")) {
+ if (device_property_read_bool(dev, "cts-override")) {
/* Always report CTS as active */
data->msr_mask_on |= UART_MSR_CTS;
data->msr_mask_off |= UART_MSR_DCTS;
}
- if (device_property_read_bool(p->dev, "ri-override")) {
+ if (device_property_read_bool(dev, "ri-override")) {
/* Always report Ring indicator as inactive */
data->msr_mask_off |= UART_MSR_RI;
data->msr_mask_off |= UART_MSR_TERI;
}
/* Always ask for fixed clock rate from a property. */
- device_property_read_u32(p->dev, "clock-frequency", &p->uartclk);
+ device_property_read_u32(dev, "clock-frequency", &p->uartclk);
/* If there is separate baudclk, get the rate from it. */
- data->clk = devm_clk_get(&pdev->dev, "baudclk");
+ data->clk = devm_clk_get(dev, "baudclk");
if (IS_ERR(data->clk) && PTR_ERR(data->clk) != -EPROBE_DEFER)
- data->clk = devm_clk_get(&pdev->dev, NULL);
+ data->clk = devm_clk_get(dev, NULL);
if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
if (!IS_ERR_OR_NULL(data->clk)) {
err = clk_prepare_enable(data->clk);
if (err)
- dev_warn(&pdev->dev, "could not enable optional baudclk: %d\n",
+ dev_warn(dev, "could not enable optional baudclk: %d\n",
err);
else
p->uartclk = clk_get_rate(data->clk);
@@ -457,24 +463,24 @@ static int dw8250_probe(struct platform_device *pdev)
/* If no clock rate is defined, fail. */
if (!p->uartclk) {
- dev_err(&pdev->dev, "clock rate not defined\n");
+ dev_err(dev, "clock rate not defined\n");
return -EINVAL;
}
- data->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
- if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) {
+ data->pclk = devm_clk_get(dev, "apb_pclk");
+ if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) {
err = -EPROBE_DEFER;
goto err_clk;
}
if (!IS_ERR(data->pclk)) {
err = clk_prepare_enable(data->pclk);
if (err) {
- dev_err(&pdev->dev, "could not enable apb_pclk\n");
+ dev_err(dev, "could not enable apb_pclk\n");
goto err_clk;
}
}
- data->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
+ data->rst = devm_reset_control_get_optional(dev, NULL);
if (IS_ERR(data->rst) && PTR_ERR(data->rst) == -EPROBE_DEFER) {
err = -EPROBE_DEFER;
goto err_pclk;
@@ -506,8 +512,8 @@ static int dw8250_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
- pm_runtime_set_active(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
return 0;
@@ -619,6 +625,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
{ "APMC0D08", 0},
{ "AMD0020", 0 },
{ "AMDI0020", 0 },
+ { "HISI0031", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c
new file mode 100644
index 0000000..886fcf3
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_lpss.c
@@ -0,0 +1,378 @@
+/*
+ * 8250_lpss.c - Driver for UART on Intel Braswell and various other Intel SoCs
+ *
+ * Copyright (C) 2016 Intel Corporation
+ * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/rational.h>
+
+#include <linux/dmaengine.h>
+#include <linux/dma/dw.h>
+
+#include "8250.h"
+
+#define PCI_DEVICE_ID_INTEL_QRK_UARTx 0x0936
+
+#define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a
+#define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c
+
+#define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a
+#define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c
+
+#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3
+#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4
+
+/* Intel LPSS specific registers */
+
+#define BYT_PRV_CLK 0x800
+#define BYT_PRV_CLK_EN BIT(0)
+#define BYT_PRV_CLK_M_VAL_SHIFT 1
+#define BYT_PRV_CLK_N_VAL_SHIFT 16
+#define BYT_PRV_CLK_UPDATE BIT(31)
+
+#define BYT_TX_OVF_INT 0x820
+#define BYT_TX_OVF_INT_MASK BIT(1)
+
+struct lpss8250;
+
+struct lpss8250_board {
+ unsigned long freq;
+ unsigned int base_baud;
+ int (*setup)(struct lpss8250 *, struct uart_port *p);
+ void (*exit)(struct lpss8250 *);
+};
+
+struct lpss8250 {
+ int line;
+ struct lpss8250_board *board;
+
+ /* DMA parameters */
+ struct uart_8250_dma dma;
+ struct dw_dma_chip dma_chip;
+ struct dw_dma_slave dma_param;
+ u8 dma_maxburst;
+};
+
+static void byt_set_termios(struct uart_port *p, struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int baud = tty_termios_baud_rate(termios);
+ struct lpss8250 *lpss = p->private_data;
+ unsigned long fref = lpss->board->freq, fuart = baud * 16;
+ unsigned long w = BIT(15) - 1;
+ unsigned long m, n;
+ u32 reg;
+
+ /* Gracefully handle the B0 case: fall back to B9600 */
+ fuart = fuart ? fuart : 9600 * 16;
+
+ /* Get Fuart closer to Fref */
+ fuart *= rounddown_pow_of_two(fref / fuart);
+
+ /*
+ * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the
+ * dividers must be adjusted.
+ *
+ * uartclk = (m / n) * 100 MHz, where m <= n
+ */
+ rational_best_approximation(fuart, fref, w, w, &m, &n);
+ p->uartclk = fuart;
+
+ /* Reset the clock */
+ reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
+ writel(reg, p->membase + BYT_PRV_CLK);
+ reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE;
+ writel(reg, p->membase + BYT_PRV_CLK);
+
+ p->status &= ~UPSTAT_AUTOCTS;
+ if (termios->c_cflag & CRTSCTS)
+ p->status |= UPSTAT_AUTOCTS;
+
+ serial8250_do_set_termios(p, termios, old);
+}
+
+static unsigned int byt_get_mctrl(struct uart_port *port)
+{
+ unsigned int ret = serial8250_do_get_mctrl(port);
+
+ /* Force DCD and DSR signals to permanently be reported as active */
+ ret |= TIOCM_CAR | TIOCM_DSR;
+
+ return ret;
+}
+
+static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
+{
+ struct dw_dma_slave *param = &lpss->dma_param;
+ struct uart_8250_port *up = up_to_u8250p(port);
+ struct pci_dev *pdev = to_pci_dev(port->dev);
+ unsigned int dma_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
+ struct pci_dev *dma_dev = pci_get_slot(pdev->bus, dma_devfn);
+
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_INTEL_BYT_UART1:
+ case PCI_DEVICE_ID_INTEL_BSW_UART1:
+ case PCI_DEVICE_ID_INTEL_BDW_UART1:
+ param->src_id = 3;
+ param->dst_id = 2;
+ break;
+ case PCI_DEVICE_ID_INTEL_BYT_UART2:
+ case PCI_DEVICE_ID_INTEL_BSW_UART2:
+ case PCI_DEVICE_ID_INTEL_BDW_UART2:
+ param->src_id = 5;
+ param->dst_id = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ param->dma_dev = &dma_dev->dev;
+ param->m_master = 0;
+ param->p_master = 1;
+
+ /* TODO: Detect FIFO size automaticaly for DesignWare 8250 */
+ port->fifosize = 64;
+ up->tx_loadsz = 64;
+
+ lpss->dma_maxburst = 16;
+
+ port->set_termios = byt_set_termios;
+ port->get_mctrl = byt_get_mctrl;
+
+ /* Disable TX counter interrupts */
+ writel(BYT_TX_OVF_INT_MASK, port->membase + BYT_TX_OVF_INT);
+
+ return 0;
+}
+
+#ifdef CONFIG_SERIAL_8250_DMA
+static const struct dw_dma_platform_data qrk_serial_dma_pdata = {
+ .nr_channels = 2,
+ .is_private = true,
+ .is_nollp = true,
+ .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
+ .chan_priority = CHAN_PRIORITY_ASCENDING,
+ .block_size = 4095,
+ .nr_masters = 1,
+ .data_width = {4},
+};
+
+static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port)
+{
+ struct uart_8250_dma *dma = &lpss->dma;
+ struct dw_dma_chip *chip = &lpss->dma_chip;
+ struct dw_dma_slave *param = &lpss->dma_param;
+ struct pci_dev *pdev = to_pci_dev(port->dev);
+ int ret;
+
+ chip->dev = &pdev->dev;
+ chip->irq = pdev->irq;
+ chip->regs = pci_ioremap_bar(pdev, 1);
+ chip->pdata = &qrk_serial_dma_pdata;
+
+ /* Falling back to PIO mode if DMA probing fails */
+ ret = dw_dma_probe(chip);
+ if (ret)
+ return;
+
+ /* Special DMA address for UART */
+ dma->rx_dma_addr = 0xfffff000;
+ dma->tx_dma_addr = 0xfffff000;
+
+ param->dma_dev = &pdev->dev;
+ param->src_id = 0;
+ param->dst_id = 1;
+ param->hs_polarity = true;
+
+ lpss->dma_maxburst = 8;
+}
+
+static void qrk_serial_exit_dma(struct lpss8250 *lpss)
+{
+ struct dw_dma_slave *param = &lpss->dma_param;
+
+ if (!param->dma_dev)
+ return;
+ dw_dma_remove(&lpss->dma_chip);
+}
+#else /* CONFIG_SERIAL_8250_DMA */
+static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) {}
+static void qrk_serial_exit_dma(struct lpss8250 *lpss) {}
+#endif /* !CONFIG_SERIAL_8250_DMA */
+
+static int qrk_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
+{
+ struct pci_dev *pdev = to_pci_dev(port->dev);
+ int ret;
+
+ ret = pci_alloc_irq_vectors(pdev, 1, 1, 0);
+ if (ret < 0)
+ return ret;
+
+ port->irq = pci_irq_vector(pdev, 0);
+
+ qrk_serial_setup_dma(lpss, port);
+ return 0;
+}
+
+static void qrk_serial_exit(struct lpss8250 *lpss)
+{
+ qrk_serial_exit_dma(lpss);
+}
+
+static bool lpss8250_dma_filter(struct dma_chan *chan, void *param)
+{
+ struct dw_dma_slave *dws = param;
+
+ if (dws->dma_dev != chan->device->dev)
+ return false;
+
+ chan->private = dws;
+ return true;
+}
+
+static int lpss8250_dma_setup(struct lpss8250 *lpss, struct uart_8250_port *port)
+{
+ struct uart_8250_dma *dma = &lpss->dma;
+ struct dw_dma_slave *rx_param, *tx_param;
+ struct device *dev = port->port.dev;
+
+ if (!lpss->dma_param.dma_dev)
+ return 0;
+
+ rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL);
+ if (!rx_param)
+ return -ENOMEM;
+
+ tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL);
+ if (!tx_param)
+ return -ENOMEM;
+
+ *rx_param = lpss->dma_param;
+ dma->rxconf.src_maxburst = lpss->dma_maxburst;
+
+ *tx_param = lpss->dma_param;
+ dma->txconf.dst_maxburst = lpss->dma_maxburst;
+
+ dma->fn = lpss8250_dma_filter;
+ dma->rx_param = rx_param;
+ dma->tx_param = tx_param;
+
+ port->dma = dma;
+ return 0;
+}
+
+static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct uart_8250_port uart;
+ struct lpss8250 *lpss;
+ int ret;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ pci_set_master(pdev);
+
+ lpss = devm_kzalloc(&pdev->dev, sizeof(*lpss), GFP_KERNEL);
+ if (!lpss)
+ return -ENOMEM;
+
+ lpss->board = (struct lpss8250_board *)id->driver_data;
+
+ memset(&uart, 0, sizeof(struct uart_8250_port));
+
+ uart.port.dev = &pdev->dev;
+ uart.port.irq = pdev->irq;
+ uart.port.private_data = lpss;
+ uart.port.type = PORT_16550A;
+ uart.port.iotype = UPIO_MEM;
+ uart.port.regshift = 2;
+ uart.port.uartclk = lpss->board->base_baud * 16;
+ uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE;
+ uart.capabilities = UART_CAP_FIFO | UART_CAP_AFE;
+ uart.port.mapbase = pci_resource_start(pdev, 0);
+ uart.port.membase = pcim_iomap(pdev, 0, 0);
+ if (!uart.port.membase)
+ return -ENOMEM;
+
+ ret = lpss->board->setup(lpss, &uart.port);
+ if (ret)
+ return ret;
+
+ ret = lpss8250_dma_setup(lpss, &uart);
+ if (ret)
+ goto err_exit;
+
+ ret = serial8250_register_8250_port(&uart);
+ if (ret < 0)
+ goto err_exit;
+
+ lpss->line = ret;
+
+ pci_set_drvdata(pdev, lpss);
+ return 0;
+
+err_exit:
+ if (lpss->board->exit)
+ lpss->board->exit(lpss);
+ return ret;
+}
+
+static void lpss8250_remove(struct pci_dev *pdev)
+{
+ struct lpss8250 *lpss = pci_get_drvdata(pdev);
+
+ if (lpss->board->exit)
+ lpss->board->exit(lpss);
+
+ serial8250_unregister_port(lpss->line);
+}
+
+static const struct lpss8250_board byt_board = {
+ .freq = 100000000,
+ .base_baud = 2764800,
+ .setup = byt_serial_setup,
+};
+
+static const struct lpss8250_board qrk_board = {
+ .freq = 44236800,
+ .base_baud = 2764800,
+ .setup = qrk_serial_setup,
+ .exit = qrk_serial_exit,
+};
+
+#define LPSS_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board }
+
+static const struct pci_device_id pci_ids[] = {
+ LPSS_DEVICE(PCI_DEVICE_ID_INTEL_QRK_UARTx, qrk_board),
+ LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART1, byt_board),
+ LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART2, byt_board),
+ LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART1, byt_board),
+ LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART2, byt_board),
+ LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART1, byt_board),
+ LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART2, byt_board),
+ { },
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static struct pci_driver lpss8250_pci_driver = {
+ .name = "8250_lpss",
+ .id_table = pci_ids,
+ .probe = lpss8250_probe,
+ .remove = lpss8250_remove,
+};
+
+module_pci_driver(lpss8250_pci_driver);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel LPSS UART driver");
diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c
index 20c5db2..39c2324 100644
--- a/drivers/tty/serial/8250/8250_mid.c
+++ b/drivers/tty/serial/8250/8250_mid.c
@@ -99,27 +99,27 @@ static int dnv_handle_irq(struct uart_port *p)
struct uart_8250_port *up = up_to_u8250p(p);
unsigned int fisr = serial_port_in(p, INTEL_MID_UART_DNV_FISR);
u32 status;
- int ret = IRQ_NONE;
+ int ret = 0;
int err;
if (fisr & BIT(2)) {
err = hsu_dma_get_status(&mid->dma_chip, 1, &status);
if (err > 0) {
serial8250_rx_dma_flush(up);
- ret |= IRQ_HANDLED;
+ ret |= 1;
} else if (err == 0)
ret |= hsu_dma_do_irq(&mid->dma_chip, 1, status);
}
if (fisr & BIT(1)) {
err = hsu_dma_get_status(&mid->dma_chip, 0, &status);
if (err > 0)
- ret |= IRQ_HANDLED;
+ ret |= 1;
else if (err == 0)
ret |= hsu_dma_do_irq(&mid->dma_chip, 0, status);
}
if (fisr & BIT(0))
ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR));
- return ret;
+ return IRQ_RETVAL(ret);
}
#define DNV_DMA_CHAN_OFFSET 0x80
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index 3611ec9..ce0cc47 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -62,7 +62,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
*/
baud = uart_get_baud_rate(port, termios, old,
port->uartclk / 16 / 0xffff,
- port->uartclk / 16);
+ port->uartclk);
if (baud <= 115200) {
serial_port_out(port, UART_MTK_HIGHS, 0x0);
@@ -76,10 +76,6 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
quot = DIV_ROUND_UP(port->uartclk, 4 * baud);
} else {
serial_port_out(port, UART_MTK_HIGHS, 0x3);
-
- /* Set to highest baudrate supported */
- if (baud >= 1152000)
- baud = 921600;
quot = DIV_ROUND_UP(port->uartclk, 256 * baud);
}
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 38963d7..7a8b5fc 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -195,6 +195,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
switch (port_type) {
case PORT_8250 ... PORT_MAX_8250:
{
+ u32 tx_threshold;
struct uart_8250_port port8250;
memset(&port8250, 0, sizeof(port8250));
port8250.port = port;
@@ -202,6 +203,12 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
if (port.fifosize)
port8250.capabilities = UART_CAP_FIFO;
+ /* Check for TX FIFO threshold & set tx_loadsz */
+ if ((of_property_read_u32(ofdev->dev.of_node, "tx-threshold",
+ &tx_threshold) == 0) &&
+ (tx_threshold < port.fifosize))
+ port8250.tx_loadsz = port.fifosize - tx_threshold;
+
if (of_property_read_bool(ofdev->dev.of_node,
"auto-flow-control"))
port8250.capabilities |= UART_CAP_AFE;
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index bc51b32..b98c157 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -21,14 +21,10 @@
#include <linux/serial_core.h>
#include <linux/8250_pci.h>
#include <linux/bitops.h>
-#include <linux/rational.h>
#include <asm/byteorder.h>
#include <asm/io.h>
-#include <linux/dmaengine.h>
-#include <linux/platform_data/dma-dw.h>
-
#include "8250.h"
/*
@@ -1349,160 +1345,6 @@ ce4100_serial_setup(struct serial_private *priv,
return ret;
}
-#define PCI_DEVICE_ID_INTEL_BYT_UART1 0x0f0a
-#define PCI_DEVICE_ID_INTEL_BYT_UART2 0x0f0c
-
-#define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a
-#define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c
-
-#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3
-#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4
-
-#define BYT_PRV_CLK 0x800
-#define BYT_PRV_CLK_EN (1 << 0)
-#define BYT_PRV_CLK_M_VAL_SHIFT 1
-#define BYT_PRV_CLK_N_VAL_SHIFT 16
-#define BYT_PRV_CLK_UPDATE (1 << 31)
-
-#define BYT_TX_OVF_INT 0x820
-#define BYT_TX_OVF_INT_MASK (1 << 1)
-
-static void
-byt_set_termios(struct uart_port *p, struct ktermios *termios,
- struct ktermios *old)
-{
- unsigned int baud = tty_termios_baud_rate(termios);
- unsigned long fref = 100000000, fuart = baud * 16;
- unsigned long w = BIT(15) - 1;
- unsigned long m, n;
- u32 reg;
-
- /* Gracefully handle the B0 case: fall back to B9600 */
- fuart = fuart ? fuart : 9600 * 16;
-
- /* Get Fuart closer to Fref */
- fuart *= rounddown_pow_of_two(fref / fuart);
-
- /*
- * For baud rates 0.5M, 1M, 1.5M, 2M, 2.5M, 3M, 3.5M and 4M the
- * dividers must be adjusted.
- *
- * uartclk = (m / n) * 100 MHz, where m <= n
- */
- rational_best_approximation(fuart, fref, w, w, &m, &n);
- p->uartclk = fuart;
-
- /* Reset the clock */
- reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
- writel(reg, p->membase + BYT_PRV_CLK);
- reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE;
- writel(reg, p->membase + BYT_PRV_CLK);
-
- p->status &= ~UPSTAT_AUTOCTS;
- if (termios->c_cflag & CRTSCTS)
- p->status |= UPSTAT_AUTOCTS;
-
- serial8250_do_set_termios(p, termios, old);
-}
-
-static bool byt_dma_filter(struct dma_chan *chan, void *param)
-{
- struct dw_dma_slave *dws = param;
-
- if (dws->dma_dev != chan->device->dev)
- return false;
-
- chan->private = dws;
- return true;
-}
-
-static unsigned int
-byt_get_mctrl(struct uart_port *port)
-{
- unsigned int ret = serial8250_do_get_mctrl(port);
-
- /* Force DCD and DSR signals to permanently be reported as active. */
- ret |= TIOCM_CAR | TIOCM_DSR;
-
- return ret;
-}
-
-static int
-byt_serial_setup(struct serial_private *priv,
- const struct pciserial_board *board,
- struct uart_8250_port *port, int idx)
-{
- struct pci_dev *pdev = priv->dev;
- struct device *dev = port->port.dev;
- struct uart_8250_dma *dma;
- struct dw_dma_slave *tx_param, *rx_param;
- struct pci_dev *dma_dev;
- int ret;
-
- dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
- if (!dma)
- return -ENOMEM;
-
- tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL);
- if (!tx_param)
- return -ENOMEM;
-
- rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL);
- if (!rx_param)
- return -ENOMEM;
-
- switch (pdev->device) {
- case PCI_DEVICE_ID_INTEL_BYT_UART1:
- case PCI_DEVICE_ID_INTEL_BSW_UART1:
- case PCI_DEVICE_ID_INTEL_BDW_UART1:
- rx_param->src_id = 3;
- tx_param->dst_id = 2;
- break;
- case PCI_DEVICE_ID_INTEL_BYT_UART2:
- case PCI_DEVICE_ID_INTEL_BSW_UART2:
- case PCI_DEVICE_ID_INTEL_BDW_UART2:
- rx_param->src_id = 5;
- tx_param->dst_id = 4;
- break;
- default:
- return -EINVAL;
- }
-
- rx_param->m_master = 0;
- rx_param->p_master = 1;
-
- dma->rxconf.src_maxburst = 16;
-
- tx_param->m_master = 0;
- tx_param->p_master = 1;
-
- dma->txconf.dst_maxburst = 16;
-
- dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 0));
- rx_param->dma_dev = &dma_dev->dev;
- tx_param->dma_dev = &dma_dev->dev;
-
- dma->fn = byt_dma_filter;
- dma->rx_param = rx_param;
- dma->tx_param = tx_param;
-
- ret = pci_default_setup(priv, board, port, idx);
- port->port.iotype = UPIO_MEM;
- port->port.type = PORT_16550A;
- port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
- port->port.set_termios = byt_set_termios;
- port->port.get_mctrl = byt_get_mctrl;
- port->port.fifosize = 64;
- port->tx_loadsz = 64;
- port->dma = dma;
- port->capabilities = UART_CAP_FIFO | UART_CAP_AFE;
-
- /* Disable Tx counter interrupts */
- writel(BYT_TX_OVF_INT_MASK, port->port.membase + BYT_TX_OVF_INT);
-
- return ret;
-}
-
static int
pci_omegapci_setup(struct serial_private *priv,
const struct pciserial_board *board,
@@ -1741,6 +1583,19 @@ static int pci_eg20t_init(struct pci_dev *dev)
#define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358
#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358
+#define UART_EXAR_MPIOINT_7_0 0x8f /* MPIOINT[7:0] */
+#define UART_EXAR_MPIOLVL_7_0 0x90 /* MPIOLVL[7:0] */
+#define UART_EXAR_MPIO3T_7_0 0x91 /* MPIO3T[7:0] */
+#define UART_EXAR_MPIOINV_7_0 0x92 /* MPIOINV[7:0] */
+#define UART_EXAR_MPIOSEL_7_0 0x93 /* MPIOSEL[7:0] */
+#define UART_EXAR_MPIOOD_7_0 0x94 /* MPIOOD[7:0] */
+#define UART_EXAR_MPIOINT_15_8 0x95 /* MPIOINT[15:8] */
+#define UART_EXAR_MPIOLVL_15_8 0x96 /* MPIOLVL[15:8] */
+#define UART_EXAR_MPIO3T_15_8 0x97 /* MPIO3T[15:8] */
+#define UART_EXAR_MPIOINV_15_8 0x98 /* MPIOINV[15:8] */
+#define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */
+#define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */
+
static int
pci_xr17c154_setup(struct serial_private *priv,
const struct pciserial_board *board,
@@ -1783,18 +1638,18 @@ pci_xr17v35x_setup(struct serial_private *priv,
* Setup Multipurpose Input/Output pins.
*/
if (idx == 0) {
- writeb(0x00, p + 0x8f); /*MPIOINT[7:0]*/
- writeb(0x00, p + 0x90); /*MPIOLVL[7:0]*/
- writeb(0x00, p + 0x91); /*MPIO3T[7:0]*/
- writeb(0x00, p + 0x92); /*MPIOINV[7:0]*/
- writeb(0x00, p + 0x93); /*MPIOSEL[7:0]*/
- writeb(0x00, p + 0x94); /*MPIOOD[7:0]*/
- writeb(0x00, p + 0x95); /*MPIOINT[15:8]*/
- writeb(0x00, p + 0x96); /*MPIOLVL[15:8]*/
- writeb(0x00, p + 0x97); /*MPIO3T[15:8]*/
- writeb(0x00, p + 0x98); /*MPIOINV[15:8]*/
- writeb(0x00, p + 0x99); /*MPIOSEL[15:8]*/
- writeb(0x00, p + 0x9a); /*MPIOOD[15:8]*/
+ writeb(0x00, p + UART_EXAR_MPIOINT_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOLVL_7_0);
+ writeb(0x00, p + UART_EXAR_MPIO3T_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOINV_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOSEL_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOOD_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOINT_15_8);
+ writeb(0x00, p + UART_EXAR_MPIOLVL_15_8);
+ writeb(0x00, p + UART_EXAR_MPIO3T_15_8);
+ writeb(0x00, p + UART_EXAR_MPIOINV_15_8);
+ writeb(0x00, p + UART_EXAR_MPIOSEL_15_8);
+ writeb(0x00, p + UART_EXAR_MPIOOD_15_8);
}
writeb(0x00, p + UART_EXAR_8XMODE);
writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR);
@@ -1830,20 +1685,20 @@ pci_fastcom335_setup(struct serial_private *priv,
switch (priv->dev->device) {
case PCI_DEVICE_ID_COMMTECH_4222PCI335:
case PCI_DEVICE_ID_COMMTECH_4224PCI335:
- writeb(0x78, p + 0x90); /* MPIOLVL[7:0] */
- writeb(0x00, p + 0x92); /* MPIOINV[7:0] */
- writeb(0x00, p + 0x93); /* MPIOSEL[7:0] */
+ writeb(0x78, p + UART_EXAR_MPIOLVL_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOINV_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOSEL_7_0);
break;
case PCI_DEVICE_ID_COMMTECH_2324PCI335:
case PCI_DEVICE_ID_COMMTECH_2328PCI335:
- writeb(0x00, p + 0x90); /* MPIOLVL[7:0] */
- writeb(0xc0, p + 0x92); /* MPIOINV[7:0] */
- writeb(0xc0, p + 0x93); /* MPIOSEL[7:0] */
+ writeb(0x00, p + UART_EXAR_MPIOLVL_7_0);
+ writeb(0xc0, p + UART_EXAR_MPIOINV_7_0);
+ writeb(0xc0, p + UART_EXAR_MPIOSEL_7_0);
break;
}
- writeb(0x00, p + 0x8f); /* MPIOINT[7:0] */
- writeb(0x00, p + 0x91); /* MPIO3T[7:0] */
- writeb(0x00, p + 0x94); /* MPIOOD[7:0] */
+ writeb(0x00, p + UART_EXAR_MPIOINT_7_0);
+ writeb(0x00, p + UART_EXAR_MPIO3T_7_0);
+ writeb(0x00, p + UART_EXAR_MPIOOD_7_0);
}
writeb(0x00, p + UART_EXAR_8XMODE);
writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR);
@@ -1934,7 +1789,6 @@ pci_wch_ch38x_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022
#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e
-#define PCI_DEVICE_ID_INTEL_QRK_UART 0x0936
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
@@ -2078,48 +1932,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
.subdevice = PCI_ANY_ID,
.setup = kt_serial_setup,
},
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_BYT_UART1,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = byt_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_BYT_UART2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = byt_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_BSW_UART1,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = byt_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_BSW_UART2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = byt_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_BDW_UART1,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = byt_serial_setup,
- },
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_BDW_UART2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = byt_serial_setup,
- },
/*
* ITE
*/
@@ -2992,8 +2804,6 @@ enum pci_board_num_t {
pbn_ADDIDATA_PCIe_4_3906250,
pbn_ADDIDATA_PCIe_8_3906250,
pbn_ce4100_1_115200,
- pbn_byt,
- pbn_qrk,
pbn_omegapci,
pbn_NETMOS9900_2s_115200,
pbn_brcm_trumanage,
@@ -3769,18 +3579,6 @@ static struct pciserial_board pci_boards[] = {
.base_baud = 921600,
.reg_shift = 2,
},
- [pbn_byt] = {
- .flags = FL_BASE0,
- .num_ports = 1,
- .base_baud = 2764800,
- .reg_shift = 2,
- },
- [pbn_qrk] = {
- .flags = FL_BASE0,
- .num_ports = 1,
- .base_baud = 2764800,
- .reg_shift = 2,
- },
[pbn_omegapci] = {
.flags = FL_BASE0,
.num_ports = 8,
@@ -3892,6 +3690,15 @@ static const struct pci_device_id blacklist[] = {
{ PCI_VDEVICE(INTEL, 0x081d), },
{ PCI_VDEVICE(INTEL, 0x1191), },
{ PCI_VDEVICE(INTEL, 0x19d8), },
+
+ /* Intel platforms with DesignWare UART */
+ { PCI_VDEVICE(INTEL, 0x0936), },
+ { PCI_VDEVICE(INTEL, 0x0f0a), },
+ { PCI_VDEVICE(INTEL, 0x0f0c), },
+ { PCI_VDEVICE(INTEL, 0x228a), },
+ { PCI_VDEVICE(INTEL, 0x228c), },
+ { PCI_VDEVICE(INTEL, 0x9ce3), },
+ { PCI_VDEVICE(INTEL, 0x9ce4), },
};
/*
@@ -5659,41 +5466,8 @@ static struct pci_device_id serial_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ce4100_1_115200 },
- /* Intel BayTrail */
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART1,
- PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
- pbn_byt },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT_UART2,
- PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
- pbn_byt },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW_UART1,
- PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
- pbn_byt },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW_UART2,
- PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
- pbn_byt },
-
- /* Intel Broadwell */
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BDW_UART1,
- PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
- pbn_byt },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BDW_UART2,
- PCI_ANY_ID, PCI_ANY_ID,
- PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000,
- pbn_byt },
/*
- * Intel Quark x1000
- */
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_UART,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_qrk },
- /*
* Cronyx Omega PCI
*/
{ PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA,
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index bdfa659..1bfb6fd 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -178,7 +178,7 @@ static const struct serial8250_config uart_config[] = {
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
- .flags = UART_CAP_FIFO | UART_CAP_AFE,
+ .flags = UART_CAP_FIFO /* | UART_CAP_AFE */,
},
[PORT_U6_16550A] = {
.name = "U6_16550A",
@@ -585,11 +585,11 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put);
*/
int serial8250_em485_init(struct uart_8250_port *p)
{
- if (p->em485 != NULL)
+ if (p->em485)
return 0;
p->em485 = kmalloc(sizeof(struct uart_8250_em485), GFP_ATOMIC);
- if (p->em485 == NULL)
+ if (!p->em485)
return -ENOMEM;
setup_timer(&p->em485->stop_tx_timer,
@@ -619,7 +619,7 @@ EXPORT_SYMBOL_GPL(serial8250_em485_init);
*/
void serial8250_em485_destroy(struct uart_8250_port *p)
{
- if (p->em485 == NULL)
+ if (!p->em485)
return;
del_timer(&p->em485->start_tx_timer);
@@ -1402,10 +1402,8 @@ static void serial8250_stop_rx(struct uart_port *port)
static void __do_stop_tx_rs485(struct uart_8250_port *p)
{
- if (!p->em485)
- return;
-
serial8250_em485_rts_after_send(p);
+
/*
* Empty the RX FIFO, we are not interested in anything
* received during the half-duplex transmission.
@@ -1414,12 +1412,8 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p)
if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
serial8250_clear_fifos(p);
- serial8250_rpm_get(p);
-
p->ier |= UART_IER_RLSI | UART_IER_RDI;
serial_port_out(&p->port, UART_IER, p->ier);
-
- serial8250_rpm_put(p);
}
}
@@ -1429,6 +1423,7 @@ static void serial8250_em485_handle_stop_tx(unsigned long arg)
struct uart_8250_em485 *em485 = p->em485;
unsigned long flags;
+ serial8250_rpm_get(p);
spin_lock_irqsave(&p->port.lock, flags);
if (em485 &&
em485->active_timer == &em485->stop_tx_timer) {
@@ -1436,15 +1431,13 @@ static void serial8250_em485_handle_stop_tx(unsigned long arg)
em485->active_timer = NULL;
}
spin_unlock_irqrestore(&p->port.lock, flags);
+ serial8250_rpm_put(p);
}
static void __stop_tx_rs485(struct uart_8250_port *p)
{
struct uart_8250_em485 *em485 = p->em485;
- if (!em485)
- return;
-
/*
* __do_stop_tx_rs485 is going to set RTS according to config
* AND flush RX FIFO if required.
@@ -1475,7 +1468,7 @@ static inline void __stop_tx(struct uart_8250_port *p)
unsigned char lsr = serial_in(p, UART_LSR);
/*
* To provide required timeing and allow FIFO transfer,
- * __stop_tx_rs485 must be called only when both FIFO and
+ * __stop_tx_rs485() must be called only when both FIFO and
* shift register are empty. It is for device driver to enable
* interrupt on TEMT.
*/
@@ -1484,9 +1477,10 @@ static inline void __stop_tx(struct uart_8250_port *p)
del_timer(&em485->start_tx_timer);
em485->active_timer = NULL;
+
+ __stop_tx_rs485(p);
}
__do_stop_tx(p);
- __stop_tx_rs485(p);
}
static void serial8250_stop_tx(struct uart_port *port)
@@ -1876,6 +1870,30 @@ static int exar_handle_irq(struct uart_port *port)
return ret;
}
+/*
+ * Newer 16550 compatible parts such as the SC16C650 & Altera 16550 Soft IP
+ * have a programmable TX threshold that triggers the THRE interrupt in
+ * the IIR register. In this case, the THRE interrupt indicates the FIFO
+ * has space available. Load it up with tx_loadsz bytes.
+ */
+static int serial8250_tx_threshold_handle_irq(struct uart_port *port)
+{
+ unsigned long flags;
+ unsigned int iir = serial_port_in(port, UART_IIR);
+
+ /* TX Threshold IRQ triggered so load up FIFO */
+ if ((iir & UART_IIR_ID) == UART_IIR_THRI) {
+ struct uart_8250_port *up = up_to_u8250p(port);
+
+ spin_lock_irqsave(&port->lock, flags);
+ serial8250_tx_chars(up);
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+
+ iir = serial_port_in(port, UART_IIR);
+ return serial8250_handle_irq(port, iir);
+}
+
static unsigned int serial8250_tx_empty(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
@@ -1988,6 +2006,7 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
if (--tmout == 0)
break;
udelay(1);
+ touch_nmi_watchdog();
}
/* Wait up to 1s for flow control if necessary */
@@ -2164,6 +2183,25 @@ int serial8250_do_startup(struct uart_port *port)
serial_port_out(port, UART_LCR, 0);
}
+ /*
+ * For the Altera 16550 variants, set TX threshold trigger level.
+ */
+ if (((port->type == PORT_ALTR_16550_F32) ||
+ (port->type == PORT_ALTR_16550_F64) ||
+ (port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) {
+ /* Bounds checking of TX threshold (valid 0 to fifosize-2) */
+ if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) {
+ pr_err("ttyS%d TX FIFO Threshold errors, skipping\n",
+ serial_index(port));
+ } else {
+ serial_port_out(port, UART_ALTR_AFR,
+ UART_ALTR_EN_TXFIFO_LW);
+ serial_port_out(port, UART_ALTR_TX_LOW,
+ port->fifosize - up->tx_loadsz);
+ port->handle_irq = serial8250_tx_threshold_handle_irq;
+ }
+ }
+
if (port->irq) {
unsigned char iir1;
/*
@@ -2499,8 +2537,6 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
- unsigned int tolerance = port->uartclk / 100;
-
/*
* Ask the core to calculate the divisor for us.
* Allow 1% tolerance at the upper limit so uart clks marginally
@@ -2509,7 +2545,7 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port,
*/
return uart_get_baud_rate(port, termios, old,
port->uartclk / 16 / 0xffff,
- (port->uartclk + tolerance) / 16);
+ port->uartclk);
}
void
@@ -2546,12 +2582,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
/*
* MCR-based auto flow control. When AFE is enabled, RTS will be
* deasserted when the receive FIFO contains more characters than
- * the trigger, or the MCR RTS bit is cleared. In the case where
- * the remote UART is not using CTS auto flow control, we must
- * have sufficient FIFO entries for the latency of the remote
- * UART to respond. IOW, at least 32 bytes of FIFO.
+ * the trigger, or the MCR RTS bit is cleared.
*/
- if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) {
+ if (up->capabilities & UART_CAP_AFE) {
up->mcr &= ~UART_MCR_AFE;
if (termios->c_cflag & CRTSCTS)
up->mcr |= UART_MCR_AFE;
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 7c6f7af..8998347 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -120,7 +120,6 @@ config SERIAL_8250_PCI
tristate "8250/16550 PCI device support" if EXPERT
depends on SERIAL_8250 && PCI
default SERIAL_8250
- select RATIONAL
help
This builds standard PCI serial support. You may be able to
disable this feature if you only need legacy serial support.
@@ -402,6 +401,21 @@ config SERIAL_8250_INGENIC
If you have a system using an Ingenic SoC and wish to make use of
its UARTs, say Y to this option. If unsure, say N.
+config SERIAL_8250_LPSS
+ tristate "Support for serial ports on Intel LPSS platforms" if EXPERT
+ default SERIAL_8250
+ depends on SERIAL_8250 && PCI
+ depends on X86 || COMPILE_TEST
+ select DW_DMAC_CORE if SERIAL_8250_DMA
+ select DW_DMAC_PCI if (SERIAL_8250_DMA && X86_INTEL_LPSS)
+ select RATIONAL
+ help
+ Selecting this option will enable handling of the extra features
+ present on the UART found on various Intel platforms such as:
+ - Intel Baytrail SoC
+ - Intel Braswell SoC
+ - Intel Quark X1000 SoC
+
config SERIAL_8250_MID
tristate "Support for serial ports on Intel MID platforms" if EXPERT
default SERIAL_8250
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 367d403..276c6fb 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o
obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o
obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
+obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
obj-$(CONFIG_SERIAL_8250_MOXA) += 8250_moxa.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 518db24a..c783140 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1380,7 +1380,7 @@ config SERIAL_IFX6X60
config SERIAL_PCH_UART
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) UART"
- depends on PCI && (X86_32 || COMPILE_TEST)
+ depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
select SERIAL_CORE
help
This driver is for PCH(Platform controller Hub) UART of Intel EG20T
diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c
index 32df2a0..e409d7d 100644
--- a/drivers/tty/serial/altera_jtaguart.c
+++ b/drivers/tty/serial/altera_jtaguart.c
@@ -280,7 +280,7 @@ static int altera_jtaguart_verify_port(struct uart_port *port,
/*
* Define the basic serial functions we support.
*/
-static struct uart_ops altera_jtaguart_ops = {
+static const struct uart_ops altera_jtaguart_ops = {
.tx_empty = altera_jtaguart_tx_empty,
.get_mctrl = altera_jtaguart_get_mctrl,
.set_mctrl = altera_jtaguart_set_mctrl,
diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c
index 61b607f..820a742 100644
--- a/drivers/tty/serial/altera_uart.c
+++ b/drivers/tty/serial/altera_uart.c
@@ -404,7 +404,7 @@ static void altera_uart_poll_put_char(struct uart_port *port, unsigned char c)
/*
* Define the basic serial functions we support.
*/
-static struct uart_ops altera_uart_ops = {
+static const struct uart_ops altera_uart_ops = {
.tx_empty = altera_uart_tx_empty,
.get_mctrl = altera_uart_get_mctrl,
.set_mctrl = altera_uart_set_mctrl,
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 8a9e213..e2c33b9 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -93,6 +93,10 @@ static u16 pl011_std_offsets[REG_ARRAY_SIZE] = {
struct vendor_data {
const u16 *reg_offset;
unsigned int ifls;
+ unsigned int fr_busy;
+ unsigned int fr_dsr;
+ unsigned int fr_cts;
+ unsigned int fr_ri;
bool access_32b;
bool oversampling;
bool dma_threshold;
@@ -111,6 +115,10 @@ static unsigned int get_fifosize_arm(struct amba_device *dev)
static struct vendor_data vendor_arm = {
.reg_offset = pl011_std_offsets,
.ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
+ .fr_busy = UART01x_FR_BUSY,
+ .fr_dsr = UART01x_FR_DSR,
+ .fr_cts = UART01x_FR_CTS,
+ .fr_ri = UART011_FR_RI,
.oversampling = false,
.dma_threshold = false,
.cts_event_workaround = false,
@@ -121,6 +129,10 @@ static struct vendor_data vendor_arm = {
static struct vendor_data vendor_sbsa = {
.reg_offset = pl011_std_offsets,
+ .fr_busy = UART01x_FR_BUSY,
+ .fr_dsr = UART01x_FR_DSR,
+ .fr_cts = UART01x_FR_CTS,
+ .fr_ri = UART011_FR_RI,
.access_32b = true,
.oversampling = false,
.dma_threshold = false,
@@ -164,6 +176,10 @@ static unsigned int get_fifosize_st(struct amba_device *dev)
static struct vendor_data vendor_st = {
.reg_offset = pl011_st_offsets,
.ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
+ .fr_busy = UART01x_FR_BUSY,
+ .fr_dsr = UART01x_FR_DSR,
+ .fr_cts = UART01x_FR_CTS,
+ .fr_ri = UART011_FR_RI,
.oversampling = true,
.dma_threshold = true,
.cts_event_workaround = true,
@@ -188,11 +204,20 @@ static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = {
[REG_DMACR] = ZX_UART011_DMACR,
};
-static struct vendor_data vendor_zte __maybe_unused = {
+static unsigned int get_fifosize_zte(struct amba_device *dev)
+{
+ return 16;
+}
+
+static struct vendor_data vendor_zte = {
.reg_offset = pl011_zte_offsets,
.access_32b = true,
.ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
- .get_fifosize = get_fifosize_arm,
+ .fr_busy = ZX_UART01x_FR_BUSY,
+ .fr_dsr = ZX_UART01x_FR_DSR,
+ .fr_cts = ZX_UART01x_FR_CTS,
+ .fr_ri = ZX_UART011_FR_RI,
+ .get_fifosize = get_fifosize_zte,
};
/* Deals with DMA transactions */
@@ -1167,7 +1192,7 @@ static void pl011_dma_shutdown(struct uart_amba_port *uap)
return;
/* Disable RX and TX DMA */
- while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY)
+ while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy)
cpu_relax();
spin_lock_irq(&uap->port.lock);
@@ -1416,11 +1441,12 @@ static void pl011_modem_status(struct uart_amba_port *uap)
if (delta & UART01x_FR_DCD)
uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD);
- if (delta & UART01x_FR_DSR)
+ if (delta & uap->vendor->fr_dsr)
uap->port.icount.dsr++;
- if (delta & UART01x_FR_CTS)
- uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
+ if (delta & uap->vendor->fr_cts)
+ uart_handle_cts_change(&uap->port,
+ status & uap->vendor->fr_cts);
wake_up_interruptible(&uap->port.state->port.delta_msr_wait);
}
@@ -1493,7 +1519,8 @@ static unsigned int pl011_tx_empty(struct uart_port *port)
struct uart_amba_port *uap =
container_of(port, struct uart_amba_port, port);
unsigned int status = pl011_read(uap, REG_FR);
- return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
+ return status & (uap->vendor->fr_busy | UART01x_FR_TXFF) ?
+ 0 : TIOCSER_TEMT;
}
static unsigned int pl011_get_mctrl(struct uart_port *port)
@@ -1508,9 +1535,9 @@ static unsigned int pl011_get_mctrl(struct uart_port *port)
result |= tiocmbit
TIOCMBIT(UART01x_FR_DCD, TIOCM_CAR);
- TIOCMBIT(UART01x_FR_DSR, TIOCM_DSR);
- TIOCMBIT(UART01x_FR_CTS, TIOCM_CTS);
- TIOCMBIT(UART011_FR_RI, TIOCM_RNG);
+ TIOCMBIT(uap->vendor->fr_dsr, TIOCM_DSR);
+ TIOCMBIT(uap->vendor->fr_cts, TIOCM_CTS);
+ TIOCMBIT(uap->vendor->fr_ri, TIOCM_RNG);
#undef TIOCMBIT
return result;
}
@@ -2191,7 +2218,7 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
* Finally, wait for transmitter to become empty
* and restore the TCR
*/
- while (pl011_read(uap, REG_FR) & UART01x_FR_BUSY)
+ while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy)
cpu_relax();
if (!uap->vendor->always_enabled)
pl011_write(old_cr, uap, REG_CR);
@@ -2555,7 +2582,8 @@ static int sbsa_uart_probe(struct platform_device *pdev)
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
- dev_err(&pdev->dev, "cannot obtain irq\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "cannot obtain irq\n");
return ret;
}
uap->port.irq = ret;
@@ -2622,6 +2650,11 @@ static struct amba_id pl011_ids[] = {
.mask = 0x00ffffff,
.data = &vendor_st,
},
+ {
+ .id = AMBA_LINUX_ID(0x00, 0x1, 0xffe),
+ .mask = 0x00ffffff,
+ .data = &vendor_zte,
+ },
{ 0, 0 },
};
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index 3a1de5c..5ac06fc 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -464,7 +464,7 @@ static int arc_serial_poll_getchar(struct uart_port *port)
}
#endif
-static struct uart_ops arc_serial_pops = {
+static const struct uart_ops arc_serial_pops = {
.tx_empty = arc_serial_tx_empty,
.set_mctrl = arc_serial_set_mctrl,
.get_mctrl = arc_serial_get_mctrl,
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 2eaa18d..fd8aa1f 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -166,6 +166,7 @@ struct atmel_uart_port {
u32 rts_low;
bool ms_irq_enabled;
u32 rtor; /* address of receiver timeout register if it exists */
+ bool has_frac_baudrate;
bool has_hw_timer;
struct timer_list uart_timer;
@@ -1634,8 +1635,8 @@ static void atmel_init_property(struct atmel_uart_port *atmel_port,
if (np) {
/* DMA/PDC usage specification */
- if (of_get_property(np, "atmel,use-dma-rx", NULL)) {
- if (of_get_property(np, "dmas", NULL)) {
+ if (of_property_read_bool(np, "atmel,use-dma-rx")) {
+ if (of_property_read_bool(np, "dmas")) {
atmel_port->use_dma_rx = true;
atmel_port->use_pdc_rx = false;
} else {
@@ -1647,8 +1648,8 @@ static void atmel_init_property(struct atmel_uart_port *atmel_port,
atmel_port->use_pdc_rx = false;
}
- if (of_get_property(np, "atmel,use-dma-tx", NULL)) {
- if (of_get_property(np, "dmas", NULL)) {
+ if (of_property_read_bool(np, "atmel,use-dma-tx")) {
+ if (of_property_read_bool(np, "dmas")) {
atmel_port->use_dma_tx = true;
atmel_port->use_pdc_tx = false;
} else {
@@ -1745,6 +1746,11 @@ static void atmel_get_ip_name(struct uart_port *port)
dbgu_uart = 0x44424755; /* DBGU */
new_uart = 0x55415254; /* UART */
+ /*
+ * Only USART devices from at91sam9260 SOC implement fractional
+ * baudrate.
+ */
+ atmel_port->has_frac_baudrate = false;
atmel_port->has_hw_timer = false;
if (name == new_uart) {
@@ -1753,6 +1759,7 @@ static void atmel_get_ip_name(struct uart_port *port)
atmel_port->rtor = ATMEL_UA_RTOR;
} else if (name == usart) {
dev_dbg(port->dev, "Usart\n");
+ atmel_port->has_frac_baudrate = true;
atmel_port->has_hw_timer = true;
atmel_port->rtor = ATMEL_US_RTOR;
} else if (name == dbgu_uart) {
@@ -1764,6 +1771,7 @@ static void atmel_get_ip_name(struct uart_port *port)
case 0x302:
case 0x10213:
dev_dbg(port->dev, "This version is usart\n");
+ atmel_port->has_frac_baudrate = true;
atmel_port->has_hw_timer = true;
atmel_port->rtor = ATMEL_US_RTOR;
break;
@@ -1929,6 +1937,9 @@ static void atmel_shutdown(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ /* Disable modem control lines interrupts */
+ atmel_disable_ms(port);
+
/* Disable interrupts at device level */
atmel_uart_writel(port, ATMEL_US_IDR, -1);
@@ -1979,8 +1990,6 @@ static void atmel_shutdown(struct uart_port *port)
*/
free_irq(port->irq, port);
- atmel_port->ms_irq_enabled = false;
-
atmel_flush_buffer(port);
}
@@ -2025,8 +2034,9 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
unsigned long flags;
- unsigned int old_mode, mode, imr, quot, baud;
+ unsigned int old_mode, mode, imr, quot, baud, div, cd, fp = 0;
/* save the current mode register */
mode = old_mode = atmel_uart_readl(port, ATMEL_US_MR);
@@ -2036,12 +2046,6 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
ATMEL_US_PAR | ATMEL_US_USMODE);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
- quot = uart_get_divisor(port, baud);
-
- if (quot > 65535) { /* BRGR is 16-bit, so switch to slower clock */
- quot /= 8;
- mode |= ATMEL_US_USCLKS_MCK_DIV8;
- }
/* byte size */
switch (termios->c_cflag & CSIZE) {
@@ -2160,7 +2164,31 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
atmel_uart_writel(port, ATMEL_US_CR, rts_state);
}
- /* set the baud rate */
+ /*
+ * Set the baud rate:
+ * Fractional baudrate allows to setup output frequency more
+ * accurately. This feature is enabled only when using normal mode.
+ * baudrate = selected clock / (8 * (2 - OVER) * (CD + FP / 8))
+ * Currently, OVER is always set to 0 so we get
+ * baudrate = selected clock / (16 * (CD + FP / 8))
+ * then
+ * 8 CD + FP = selected clock / (2 * baudrate)
+ */
+ if (atmel_port->has_frac_baudrate &&
+ (mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_NORMAL) {
+ div = DIV_ROUND_CLOSEST(port->uartclk, baud * 2);
+ cd = div >> 3;
+ fp = div & ATMEL_US_FP_MASK;
+ } else {
+ cd = uart_get_divisor(port, baud);
+ }
+
+ if (cd > 65535) { /* BRGR is 16-bit, so switch to slower clock */
+ cd /= 8;
+ mode |= ATMEL_US_USCLKS_MCK_DIV8;
+ }
+ quot = cd | fp << ATMEL_US_FP_OFFSET;
+
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
@@ -2292,7 +2320,7 @@ static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
}
#endif
-static struct uart_ops atmel_pops = {
+static const struct uart_ops atmel_pops = {
.tx_empty = atmel_tx_empty,
.set_mctrl = atmel_set_mctrl,
.get_mctrl = atmel_get_mctrl,
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index 5108fab..583c9a0 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -631,7 +631,7 @@ static int bcm_uart_verify_port(struct uart_port *port,
}
/* serial core callbacks */
-static struct uart_ops bcm_uart_ops = {
+static const struct uart_ops bcm_uart_ops = {
.tx_empty = bcm_uart_tx_empty,
.get_mctrl = bcm_uart_get_mctrl,
.set_mctrl = bcm_uart_set_mctrl,
diff --git a/drivers/tty/serial/earlycon-arm-semihost.c b/drivers/tty/serial/earlycon-arm-semihost.c
index 383db10..6bbeb69 100644
--- a/drivers/tty/serial/earlycon-arm-semihost.c
+++ b/drivers/tty/serial/earlycon-arm-semihost.c
@@ -53,7 +53,8 @@ static void smh_write(struct console *con, const char *s, unsigned n)
uart_console_write(&dev->port, s, n, smh_putc);
}
-int __init early_smh_setup(struct earlycon_device *device, const char *opt)
+static int
+__init early_smh_setup(struct earlycon_device *device, const char *opt)
{
device->con->write = smh_write;
return 0;
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index 067783f..c365154 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -21,6 +21,7 @@
#include <linux/sizes.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
+#include <linux/acpi.h>
#ifdef CONFIG_FIX_EARLYCON_MEM
#include <asm/fixmap.h>
@@ -38,7 +39,7 @@ static struct earlycon_device early_console_dev = {
.con = &early_con,
};
-static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
+static void __iomem * __init earlycon_map(resource_size_t paddr, size_t size)
{
void __iomem *base;
#ifdef CONFIG_FIX_EARLYCON_MEM
@@ -49,8 +50,7 @@ static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
base = ioremap(paddr, size);
#endif
if (!base)
- pr_err("%s: Couldn't map 0x%llx\n", __func__,
- (unsigned long long)paddr);
+ pr_err("%s: Couldn't map %pa\n", __func__, &paddr);
return base;
}
@@ -92,7 +92,7 @@ static int __init parse_options(struct earlycon_device *device, char *options)
{
struct uart_port *port = &device->port;
int length;
- unsigned long addr;
+ resource_size_t addr;
if (uart_parse_earlycon(options, &port->iotype, &addr, &options))
return -EINVAL;
@@ -199,6 +199,14 @@ int __init setup_earlycon(char *buf)
return -ENOENT;
}
+/*
+ * When CONFIG_ACPI_SPCR_TABLE is defined, "earlycon" without parameters in
+ * command line does not start DT earlycon immediately, instead it defers
+ * starting it until DT/ACPI decision is made. At that time if ACPI is enabled
+ * call parse_spcr(), else call early_init_dt_scan_chosen_stdout()
+ */
+bool earlycon_init_is_deferred __initdata;
+
/* early_param wrapper for setup_earlycon() */
static int __init param_setup_earlycon(char *buf)
{
@@ -208,8 +216,14 @@ static int __init param_setup_earlycon(char *buf)
* Just 'earlycon' is a valid param for devicetree earlycons;
* don't generate a warning from parse_early_params() in that case
*/
- if (!buf || !buf[0])
- return 0;
+ if (!buf || !buf[0]) {
+ if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
+ earlycon_init_is_deferred = true;
+ return 0;
+ } else {
+ return early_init_dt_scan_chosen_stdout();
+ }
+ }
err = setup_earlycon(buf);
if (err == -ENOENT || err == -EALREADY)
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 7f95f78..de9d510 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -224,7 +224,8 @@
#define UARTWATER_TXWATER_OFF 0
#define UARTWATER_RXWATER_OFF 16
-#define FSL_UART_RX_DMA_BUFFER_SIZE 64
+/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
+#define DMA_RX_TIMEOUT (10)
#define DRIVER_NAME "fsl-lpuart"
#define DEV_NAME "ttyLP"
@@ -243,18 +244,18 @@ struct lpuart_port {
struct dma_chan *dma_rx_chan;
struct dma_async_tx_descriptor *dma_tx_desc;
struct dma_async_tx_descriptor *dma_rx_desc;
- dma_addr_t dma_tx_buf_bus;
- dma_addr_t dma_rx_buf_bus;
dma_cookie_t dma_tx_cookie;
dma_cookie_t dma_rx_cookie;
- unsigned char *dma_tx_buf_virt;
- unsigned char *dma_rx_buf_virt;
unsigned int dma_tx_bytes;
unsigned int dma_rx_bytes;
- int dma_tx_in_progress;
- int dma_rx_in_progress;
+ bool dma_tx_in_progress;
unsigned int dma_rx_timeout;
struct timer_list lpuart_timer;
+ struct scatterlist rx_sgl, tx_sgl[2];
+ struct circ_buf rx_ring;
+ int rx_dma_rng_buf_len;
+ unsigned int dma_tx_nents;
+ wait_queue_head_t dma_wait;
};
static const struct of_device_id lpuart_dt_ids[] = {
@@ -270,7 +271,6 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
/* Forward declare this for the dma callbacks*/
static void lpuart_dma_tx_complete(void *arg);
-static void lpuart_dma_rx_complete(void *arg);
static u32 lpuart32_read(void __iomem *addr)
{
@@ -316,141 +316,103 @@ static void lpuart32_stop_rx(struct uart_port *port)
lpuart32_write(temp & ~UARTCTRL_RE, port->membase + UARTCTRL);
}
-static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
- struct tty_port *tty, int count)
+static void lpuart_dma_tx(struct lpuart_port *sport)
{
- int copied;
-
- sport->port.icount.rx += count;
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ struct scatterlist *sgl = sport->tx_sgl;
+ struct device *dev = sport->port.dev;
+ int ret;
- if (!tty) {
- dev_err(sport->port.dev, "No tty port\n");
+ if (sport->dma_tx_in_progress)
return;
- }
- dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
- FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
- copied = tty_insert_flip_string(tty,
- ((unsigned char *)(sport->dma_rx_buf_virt)), count);
+ sport->dma_tx_bytes = uart_circ_chars_pending(xmit);
- if (copied != count) {
- WARN_ON(1);
- dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
+ if (xmit->tail < xmit->head) {
+ sport->dma_tx_nents = 1;
+ sg_init_one(sgl, xmit->buf + xmit->tail, sport->dma_tx_bytes);
+ } else {
+ sport->dma_tx_nents = 2;
+ sg_init_table(sgl, 2);
+ sg_set_buf(sgl, xmit->buf + xmit->tail,
+ UART_XMIT_SIZE - xmit->tail);
+ sg_set_buf(sgl + 1, xmit->buf, xmit->head);
}
- dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
- FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-}
-
-static void lpuart_pio_tx(struct lpuart_port *sport)
-{
- struct circ_buf *xmit = &sport->port.state->xmit;
- unsigned long flags;
-
- spin_lock_irqsave(&sport->port.lock, flags);
-
- while (!uart_circ_empty(xmit) &&
- readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
- writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- sport->port.icount.tx++;
+ ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+ if (!ret) {
+ dev_err(dev, "DMA mapping error for TX.\n");
+ return;
}
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&sport->port);
-
- if (uart_circ_empty(xmit))
- writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
- sport->port.membase + UARTCR5);
-
- spin_unlock_irqrestore(&sport->port.lock, flags);
-}
-
-static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
-{
- struct circ_buf *xmit = &sport->port.state->xmit;
- dma_addr_t tx_bus_addr;
-
- dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
- UART_XMIT_SIZE, DMA_TO_DEVICE);
- sport->dma_tx_bytes = count & ~(sport->txfifo_size - 1);
- tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
- sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
- tx_bus_addr, sport->dma_tx_bytes,
+ sport->dma_tx_desc = dmaengine_prep_slave_sg(sport->dma_tx_chan, sgl,
+ sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
-
if (!sport->dma_tx_desc) {
- dev_err(sport->port.dev, "Not able to get desc for tx\n");
- return -EIO;
+ dma_unmap_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+ dev_err(dev, "Cannot prepare TX slave DMA!\n");
+ return;
}
sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
sport->dma_tx_desc->callback_param = sport;
- sport->dma_tx_in_progress = 1;
+ sport->dma_tx_in_progress = true;
sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
dma_async_issue_pending(sport->dma_tx_chan);
- return 0;
-}
-
-static void lpuart_prepare_tx(struct lpuart_port *sport)
-{
- struct circ_buf *xmit = &sport->port.state->xmit;
- unsigned long count = CIRC_CNT_TO_END(xmit->head,
- xmit->tail, UART_XMIT_SIZE);
-
- if (!count)
- return;
-
- if (count < sport->txfifo_size)
- writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS,
- sport->port.membase + UARTCR5);
- else {
- writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
- sport->port.membase + UARTCR5);
- lpuart_dma_tx(sport, count);
- }
}
static void lpuart_dma_tx_complete(void *arg)
{
struct lpuart_port *sport = arg;
+ struct scatterlist *sgl = &sport->tx_sgl[0];
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
- async_tx_ack(sport->dma_tx_desc);
-
spin_lock_irqsave(&sport->port.lock, flags);
+ dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+
xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
- sport->dma_tx_in_progress = 0;
+
+ sport->port.icount.tx += sport->dma_tx_bytes;
+ sport->dma_tx_in_progress = false;
+ spin_unlock_irqrestore(&sport->port.lock, flags);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);
- lpuart_prepare_tx(sport);
+ if (waitqueue_active(&sport->dma_wait)) {
+ wake_up(&sport->dma_wait);
+ return;
+ }
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
+ lpuart_dma_tx(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
-static int lpuart_dma_rx(struct lpuart_port *sport)
+static int lpuart_dma_tx_request(struct uart_port *port)
{
- dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
- FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
- sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
- sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
- DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+ struct dma_slave_config dma_tx_sconfig = {};
+ int ret;
- if (!sport->dma_rx_desc) {
- dev_err(sport->port.dev, "Not able to get desc for rx\n");
- return -EIO;
- }
+ dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
+ dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_tx_sconfig.dst_maxburst = 1;
+ dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
- sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
- sport->dma_rx_desc->callback_param = sport;
- sport->dma_rx_in_progress = 1;
- sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
- dma_async_issue_pending(sport->dma_rx_chan);
+ if (ret) {
+ dev_err(sport->port.dev,
+ "DMA slave config failed, err = %d\n", ret);
+ return ret;
+ }
return 0;
}
@@ -458,75 +420,17 @@ static int lpuart_dma_rx(struct lpuart_port *sport)
static void lpuart_flush_buffer(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+
if (sport->lpuart_dma_tx_use) {
+ if (sport->dma_tx_in_progress) {
+ dma_unmap_sg(sport->port.dev, &sport->tx_sgl[0],
+ sport->dma_tx_nents, DMA_TO_DEVICE);
+ sport->dma_tx_in_progress = false;
+ }
dmaengine_terminate_all(sport->dma_tx_chan);
- sport->dma_tx_in_progress = 0;
}
}
-static void lpuart_dma_rx_complete(void *arg)
-{
- struct lpuart_port *sport = arg;
- struct tty_port *port = &sport->port.state->port;
- unsigned long flags;
-
- async_tx_ack(sport->dma_rx_desc);
- mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
-
- spin_lock_irqsave(&sport->port.lock, flags);
-
- sport->dma_rx_in_progress = 0;
- lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE);
- tty_flip_buffer_push(port);
- lpuart_dma_rx(sport);
-
- spin_unlock_irqrestore(&sport->port.lock, flags);
-}
-
-static void lpuart_timer_func(unsigned long data)
-{
- struct lpuart_port *sport = (struct lpuart_port *)data;
- struct tty_port *port = &sport->port.state->port;
- struct dma_tx_state state;
- unsigned long flags;
- unsigned char temp;
- int count;
-
- del_timer(&sport->lpuart_timer);
- dmaengine_pause(sport->dma_rx_chan);
- dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state);
- dmaengine_terminate_all(sport->dma_rx_chan);
- count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue;
- async_tx_ack(sport->dma_rx_desc);
-
- spin_lock_irqsave(&sport->port.lock, flags);
-
- sport->dma_rx_in_progress = 0;
- lpuart_copy_rx_to_tty(sport, port, count);
- tty_flip_buffer_push(port);
- temp = readb(sport->port.membase + UARTCR5);
- writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
-
- spin_unlock_irqrestore(&sport->port.lock, flags);
-}
-
-static inline void lpuart_prepare_rx(struct lpuart_port *sport)
-{
- unsigned long flags;
- unsigned char temp;
-
- spin_lock_irqsave(&sport->port.lock, flags);
-
- sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
- add_timer(&sport->lpuart_timer);
-
- lpuart_dma_rx(sport);
- temp = readb(sport->port.membase + UARTCR5);
- writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5);
-
- spin_unlock_irqrestore(&sport->port.lock, flags);
-}
-
static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
@@ -580,8 +484,8 @@ static void lpuart_start_tx(struct uart_port *port)
writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
if (sport->lpuart_dma_tx_use) {
- if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
- lpuart_prepare_tx(sport);
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(port))
+ lpuart_dma_tx(sport);
} else {
if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
lpuart_transmit_buffer(sport);
@@ -600,6 +504,29 @@ static void lpuart32_start_tx(struct uart_port *port)
lpuart32_transmit_buffer(sport);
}
+/* return TIOCSER_TEMT when transmitter is not busy */
+static unsigned int lpuart_tx_empty(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+ unsigned char sr1 = readb(port->membase + UARTSR1);
+ unsigned char sfifo = readb(port->membase + UARTSFIFO);
+
+ if (sport->dma_tx_in_progress)
+ return 0;
+
+ if (sr1 & UARTSR1_TC && sfifo & UARTSFIFO_TXEMPT)
+ return TIOCSER_TEMT;
+
+ return 0;
+}
+
+static unsigned int lpuart32_tx_empty(struct uart_port *port)
+{
+ return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ?
+ TIOCSER_TEMT : 0;
+}
+
static irqreturn_t lpuart_txint(int irq, void *dev_id)
{
struct lpuart_port *sport = dev_id;
@@ -766,23 +693,15 @@ out:
static irqreturn_t lpuart_int(int irq, void *dev_id)
{
struct lpuart_port *sport = dev_id;
- unsigned char sts, crdma;
+ unsigned char sts;
sts = readb(sport->port.membase + UARTSR1);
- crdma = readb(sport->port.membase + UARTCR5);
- if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) {
- if (sport->lpuart_dma_rx_use)
- lpuart_prepare_rx(sport);
- else
- lpuart_rxint(irq, dev_id);
- }
- if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) {
- if (sport->lpuart_dma_tx_use)
- lpuart_pio_tx(sport);
- else
- lpuart_txint(irq, dev_id);
- }
+ if (sts & UARTSR1_RDRF)
+ lpuart_rxint(irq, dev_id);
+
+ if (sts & UARTSR1_TDRE)
+ lpuart_txint(irq, dev_id);
return IRQ_HANDLED;
}
@@ -807,17 +726,241 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id)
return IRQ_HANDLED;
}
-/* return TIOCSER_TEMT when transmitter is not busy */
-static unsigned int lpuart_tx_empty(struct uart_port *port)
+static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
{
- return (readb(port->membase + UARTSR1) & UARTSR1_TC) ?
- TIOCSER_TEMT : 0;
+ struct tty_port *port = &sport->port.state->port;
+ struct dma_tx_state state;
+ enum dma_status dmastat;
+ struct circ_buf *ring = &sport->rx_ring;
+ unsigned long flags;
+ int count = 0;
+ unsigned char sr;
+
+ sr = readb(sport->port.membase + UARTSR1);
+
+ if (sr & (UARTSR1_PE | UARTSR1_FE)) {
+ /* Read DR to clear the error flags */
+ readb(sport->port.membase + UARTDR);
+
+ if (sr & UARTSR1_PE)
+ sport->port.icount.parity++;
+ else if (sr & UARTSR1_FE)
+ sport->port.icount.frame++;
+ }
+
+ async_tx_ack(sport->dma_rx_desc);
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ dmastat = dmaengine_tx_status(sport->dma_rx_chan,
+ sport->dma_rx_cookie,
+ &state);
+
+ if (dmastat == DMA_ERROR) {
+ dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ return;
+ }
+
+ /* CPU claims ownership of RX DMA buffer */
+ dma_sync_sg_for_cpu(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
+
+ /*
+ * ring->head points to the end of data already written by the DMA.
+ * ring->tail points to the beginning of data to be read by the
+ * framework.
+ * The current transfer size should not be larger than the dma buffer
+ * length.
+ */
+ ring->head = sport->rx_sgl.length - state.residue;
+ BUG_ON(ring->head > sport->rx_sgl.length);
+ /*
+ * At this point ring->head may point to the first byte right after the
+ * last byte of the dma buffer:
+ * 0 <= ring->head <= sport->rx_sgl.length
+ *
+ * However ring->tail must always points inside the dma buffer:
+ * 0 <= ring->tail <= sport->rx_sgl.length - 1
+ *
+ * Since we use a ring buffer, we have to handle the case
+ * where head is lower than tail. In such a case, we first read from
+ * tail to the end of the buffer then reset tail.
+ */
+ if (ring->head < ring->tail) {
+ count = sport->rx_sgl.length - ring->tail;
+
+ tty_insert_flip_string(port, ring->buf + ring->tail, count);
+ ring->tail = 0;
+ sport->port.icount.rx += count;
+ }
+
+ /* Finally we read data from tail to head */
+ if (ring->tail < ring->head) {
+ count = ring->head - ring->tail;
+ tty_insert_flip_string(port, ring->buf + ring->tail, count);
+ /* Wrap ring->head if needed */
+ if (ring->head >= sport->rx_sgl.length)
+ ring->head = 0;
+ ring->tail = ring->head;
+ sport->port.icount.rx += count;
+ }
+
+ dma_sync_sg_for_device(sport->port.dev, &sport->rx_sgl, 1,
+ DMA_FROM_DEVICE);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ tty_flip_buffer_push(port);
+ mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout);
}
-static unsigned int lpuart32_tx_empty(struct uart_port *port)
+static void lpuart_dma_rx_complete(void *arg)
{
- return (lpuart32_read(port->membase + UARTSTAT) & UARTSTAT_TC) ?
- TIOCSER_TEMT : 0;
+ struct lpuart_port *sport = arg;
+
+ lpuart_copy_rx_to_tty(sport);
+}
+
+static void lpuart_timer_func(unsigned long data)
+{
+ struct lpuart_port *sport = (struct lpuart_port *)data;
+
+ lpuart_copy_rx_to_tty(sport);
+}
+
+static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
+{
+ struct dma_slave_config dma_rx_sconfig = {};
+ struct circ_buf *ring = &sport->rx_ring;
+ int ret, nent;
+ int bits, baud;
+ struct tty_struct *tty = tty_port_tty_get(&sport->port.state->port);
+ struct ktermios *termios = &tty->termios;
+
+ baud = tty_get_baud_rate(tty);
+
+ bits = (termios->c_cflag & CSIZE) == CS7 ? 9 : 10;
+ if (termios->c_cflag & PARENB)
+ bits++;
+
+ /*
+ * Calculate length of one DMA buffer size to keep latency below
+ * 10ms at any baud rate.
+ */
+ sport->rx_dma_rng_buf_len = (DMA_RX_TIMEOUT * baud / bits / 1000) * 2;
+ sport->rx_dma_rng_buf_len = (1 << (fls(sport->rx_dma_rng_buf_len) - 1));
+ if (sport->rx_dma_rng_buf_len < 16)
+ sport->rx_dma_rng_buf_len = 16;
+
+ ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
+ if (!ring->buf) {
+ dev_err(sport->port.dev, "Ring buf alloc failed\n");
+ return -ENOMEM;
+ }
+
+ sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
+ sg_set_buf(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
+ nent = dma_map_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
+
+ if (!nent) {
+ dev_err(sport->port.dev, "DMA Rx mapping error\n");
+ return -EINVAL;
+ }
+
+ dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
+ dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma_rx_sconfig.src_maxburst = 1;
+ dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
+ ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig);
+
+ if (ret < 0) {
+ dev_err(sport->port.dev,
+ "DMA Rx slave config failed, err = %d\n", ret);
+ return ret;
+ }
+
+ sport->dma_rx_desc = dmaengine_prep_dma_cyclic(sport->dma_rx_chan,
+ sg_dma_address(&sport->rx_sgl),
+ sport->rx_sgl.length,
+ sport->rx_sgl.length / 2,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!sport->dma_rx_desc) {
+ dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
+ return -EFAULT;
+ }
+
+ sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
+ sport->dma_rx_desc->callback_param = sport;
+ sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
+ dma_async_issue_pending(sport->dma_rx_chan);
+
+ writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS,
+ sport->port.membase + UARTCR5);
+
+ return 0;
+}
+
+static void lpuart_dma_rx_free(struct uart_port *port)
+{
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+
+ if (sport->dma_rx_chan)
+ dmaengine_terminate_all(sport->dma_rx_chan);
+
+ dma_unmap_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
+ kfree(sport->rx_ring.buf);
+ sport->rx_ring.tail = 0;
+ sport->rx_ring.head = 0;
+ sport->dma_rx_desc = NULL;
+ sport->dma_rx_cookie = -EINVAL;
+}
+
+static int lpuart_config_rs485(struct uart_port *port,
+ struct serial_rs485 *rs485)
+{
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+
+ u8 modem = readb(sport->port.membase + UARTMODEM) &
+ ~(UARTMODEM_TXRTSPOL | UARTMODEM_TXRTSE);
+ writeb(modem, sport->port.membase + UARTMODEM);
+
+ if (rs485->flags & SER_RS485_ENABLED) {
+ /* Enable auto RS-485 RTS mode */
+ modem |= UARTMODEM_TXRTSE;
+
+ /*
+ * RTS needs to be logic HIGH either during transer _or_ after
+ * transfer, other variants are not supported by the hardware.
+ */
+
+ if (!(rs485->flags & (SER_RS485_RTS_ON_SEND |
+ SER_RS485_RTS_AFTER_SEND)))
+ rs485->flags |= SER_RS485_RTS_ON_SEND;
+
+ if (rs485->flags & SER_RS485_RTS_ON_SEND &&
+ rs485->flags & SER_RS485_RTS_AFTER_SEND)
+ rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+
+ /*
+ * The hardware defaults to RTS logic HIGH while transfer.
+ * Switch polarity in case RTS shall be logic HIGH
+ * after transfer.
+ * Note: UART is assumed to be active high.
+ */
+ if (rs485->flags & SER_RS485_RTS_ON_SEND)
+ modem &= ~UARTMODEM_TXRTSPOL;
+ else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+ modem |= UARTMODEM_TXRTSPOL;
+ }
+
+ /* Store the new configuration */
+ sport->port.rs485 = *rs485;
+
+ writeb(modem, sport->port.membase + UARTMODEM);
+ return 0;
}
static unsigned int lpuart_get_mctrl(struct uart_port *port)
@@ -853,17 +996,22 @@ static unsigned int lpuart32_get_mctrl(struct uart_port *port)
static void lpuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned char temp;
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
- temp = readb(port->membase + UARTMODEM) &
+ /* Make sure RXRTSE bit is not set when RS485 is enabled */
+ if (!(sport->port.rs485.flags & SER_RS485_ENABLED)) {
+ temp = readb(sport->port.membase + UARTMODEM) &
~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
- if (mctrl & TIOCM_RTS)
- temp |= UARTMODEM_RXRTSE;
+ if (mctrl & TIOCM_RTS)
+ temp |= UARTMODEM_RXRTSE;
- if (mctrl & TIOCM_CTS)
- temp |= UARTMODEM_TXCTSE;
+ if (mctrl & TIOCM_CTS)
+ temp |= UARTMODEM_TXCTSE;
- writeb(temp, port->membase + UARTMODEM);
+ writeb(temp, port->membase + UARTMODEM);
+ }
}
static void lpuart32_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -921,13 +1069,16 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
sport->port.membase + UARTPFIFO);
- /* explicitly clear RDRF */
- readb(sport->port.membase + UARTSR1);
-
/* flush Tx and Rx FIFO */
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
+ /* explicitly clear RDRF */
+ if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) {
+ readb(sport->port.membase + UARTDR);
+ writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
+ }
+
writeb(0, sport->port.membase + UARTTWFIFO);
writeb(1, sport->port.membase + UARTRWFIFO);
@@ -960,110 +1111,12 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport)
lpuart32_write(ctrl_saved, sport->port.membase + UARTCTRL);
}
-static int lpuart_dma_tx_request(struct uart_port *port)
-{
- struct lpuart_port *sport = container_of(port,
- struct lpuart_port, port);
- struct dma_slave_config dma_tx_sconfig;
- dma_addr_t dma_bus;
- unsigned char *dma_buf;
- int ret;
-
- dma_bus = dma_map_single(sport->dma_tx_chan->device->dev,
- sport->port.state->xmit.buf,
- UART_XMIT_SIZE, DMA_TO_DEVICE);
-
- if (dma_mapping_error(sport->dma_tx_chan->device->dev, dma_bus)) {
- dev_err(sport->port.dev, "dma_map_single tx failed\n");
- return -ENOMEM;
- }
-
- dma_buf = sport->port.state->xmit.buf;
- dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
- dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- dma_tx_sconfig.dst_maxburst = sport->txfifo_size;
- dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
- ret = dmaengine_slave_config(sport->dma_tx_chan, &dma_tx_sconfig);
-
- if (ret < 0) {
- dev_err(sport->port.dev,
- "Dma slave config failed, err = %d\n", ret);
- return ret;
- }
-
- sport->dma_tx_buf_virt = dma_buf;
- sport->dma_tx_buf_bus = dma_bus;
- sport->dma_tx_in_progress = 0;
-
- return 0;
-}
-
-static int lpuart_dma_rx_request(struct uart_port *port)
+static void rx_dma_timer_init(struct lpuart_port *sport)
{
- struct lpuart_port *sport = container_of(port,
- struct lpuart_port, port);
- struct dma_slave_config dma_rx_sconfig;
- dma_addr_t dma_bus;
- unsigned char *dma_buf;
- int ret;
-
- dma_buf = devm_kzalloc(sport->port.dev,
- FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
-
- if (!dma_buf) {
- dev_err(sport->port.dev, "Dma rx alloc failed\n");
- return -ENOMEM;
- }
-
- dma_bus = dma_map_single(sport->dma_rx_chan->device->dev, dma_buf,
- FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-
- if (dma_mapping_error(sport->dma_rx_chan->device->dev, dma_bus)) {
- dev_err(sport->port.dev, "dma_map_single rx failed\n");
- return -ENOMEM;
- }
-
- dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
- dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- dma_rx_sconfig.src_maxburst = 1;
- dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
- ret = dmaengine_slave_config(sport->dma_rx_chan, &dma_rx_sconfig);
-
- if (ret < 0) {
- dev_err(sport->port.dev,
- "Dma slave config failed, err = %d\n", ret);
- return ret;
- }
-
- sport->dma_rx_buf_virt = dma_buf;
- sport->dma_rx_buf_bus = dma_bus;
- sport->dma_rx_in_progress = 0;
-
- return 0;
-}
-
-static void lpuart_dma_tx_free(struct uart_port *port)
-{
- struct lpuart_port *sport = container_of(port,
- struct lpuart_port, port);
-
- dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
- UART_XMIT_SIZE, DMA_TO_DEVICE);
-
- sport->dma_tx_buf_bus = 0;
- sport->dma_tx_buf_virt = NULL;
-}
-
-static void lpuart_dma_rx_free(struct uart_port *port)
-{
- struct lpuart_port *sport = container_of(port,
- struct lpuart_port, port);
-
- dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
- FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-
- sport->dma_rx_buf_bus = 0;
- sport->dma_rx_buf_virt = NULL;
+ setup_timer(&sport->lpuart_timer, lpuart_timer_func,
+ (unsigned long)sport);
+ sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
+ add_timer(&sport->lpuart_timer);
}
static int lpuart_startup(struct uart_port *port)
@@ -1084,22 +1137,6 @@ static int lpuart_startup(struct uart_port *port)
sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
UARTPFIFO_FIFOSIZE_MASK) + 1);
- if (sport->dma_rx_chan && !lpuart_dma_rx_request(port)) {
- sport->lpuart_dma_rx_use = true;
- setup_timer(&sport->lpuart_timer, lpuart_timer_func,
- (unsigned long)sport);
- } else
- sport->lpuart_dma_rx_use = false;
-
-
- if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
- sport->lpuart_dma_tx_use = true;
- temp = readb(port->membase + UARTCR5);
- temp &= ~UARTCR5_RDMAS;
- writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
- } else
- sport->lpuart_dma_tx_use = false;
-
ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
DRIVER_NAME, sport);
if (ret)
@@ -1113,7 +1150,29 @@ static int lpuart_startup(struct uart_port *port)
temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
writeb(temp, sport->port.membase + UARTCR2);
+ if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
+ /* set Rx DMA timeout */
+ sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
+ if (!sport->dma_rx_timeout)
+ sport->dma_rx_timeout = 1;
+
+ sport->lpuart_dma_rx_use = true;
+ rx_dma_timer_init(sport);
+ } else {
+ sport->lpuart_dma_rx_use = false;
+ }
+
+ if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
+ init_waitqueue_head(&sport->dma_wait);
+ sport->lpuart_dma_tx_use = true;
+ temp = readb(port->membase + UARTCR5);
+ writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
+ } else {
+ sport->lpuart_dma_tx_use = false;
+ }
+
spin_unlock_irqrestore(&sport->port.lock, flags);
+
return 0;
}
@@ -1170,12 +1229,19 @@ static void lpuart_shutdown(struct uart_port *port)
devm_free_irq(port->dev, port->irq, sport);
if (sport->lpuart_dma_rx_use) {
- lpuart_dma_rx_free(&sport->port);
del_timer_sync(&sport->lpuart_timer);
+ lpuart_dma_rx_free(&sport->port);
}
- if (sport->lpuart_dma_tx_use)
- lpuart_dma_tx_free(&sport->port);
+ if (sport->lpuart_dma_tx_use) {
+ if (wait_event_interruptible(sport->dma_wait,
+ !sport->dma_tx_in_progress) != false) {
+ sport->dma_tx_in_progress = false;
+ dmaengine_terminate_all(sport->dma_tx_chan);
+ }
+
+ lpuart_stop_tx(port);
+ }
}
static void lpuart32_shutdown(struct uart_port *port)
@@ -1203,13 +1269,14 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
unsigned long flags;
- unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+ unsigned char cr1, old_cr1, old_cr2, cr3, cr4, bdh, modem;
unsigned int baud;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned int sbr, brfa;
cr1 = old_cr1 = readb(sport->port.membase + UARTCR1);
old_cr2 = readb(sport->port.membase + UARTCR2);
+ cr3 = readb(sport->port.membase + UARTCR3);
cr4 = readb(sport->port.membase + UARTCR4);
bdh = readb(sport->port.membase + UARTBDH);
modem = readb(sport->port.membase + UARTMODEM);
@@ -1240,6 +1307,13 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
cr1 |= UARTCR1_M;
}
+ /*
+ * When auto RS-485 RTS mode is enabled,
+ * hardware flow control need to be disabled.
+ */
+ if (sport->port.rs485.flags & SER_RS485_ENABLED)
+ termios->c_cflag &= ~CRTSCTS;
+
if (termios->c_cflag & CRTSCTS) {
modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
} else {
@@ -1257,7 +1331,10 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
if ((termios->c_cflag & PARENB)) {
if (termios->c_cflag & CMSPAR) {
cr1 &= ~UARTCR1_PE;
- cr1 |= UARTCR1_M;
+ if (termios->c_cflag & PARODD)
+ cr3 |= UARTCR3_T8;
+ else
+ cr3 &= ~UARTCR3_T8;
} else {
cr1 |= UARTCR1_PE;
if ((termios->c_cflag & CSIZE) == CS8)
@@ -1297,17 +1374,6 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
/* update the per-port timeout */
uart_update_timeout(port, termios->c_cflag, baud);
- if (sport->lpuart_dma_rx_use) {
- /* Calculate delay for 1.5 DMA buffers */
- sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
- FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
- sport->rxfifo_size / 2;
- dev_dbg(port->dev, "DMA Rx t-out %ums, tty t-out %u jiffies\n",
- sport->dma_rx_timeout * 1000 / HZ, sport->port.timeout);
- if (sport->dma_rx_timeout < msecs_to_jiffies(20))
- sport->dma_rx_timeout = msecs_to_jiffies(20);
- }
-
/* wait transmit engin complete */
while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
barrier();
@@ -1325,12 +1391,31 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
writeb(cr4 | brfa, sport->port.membase + UARTCR4);
writeb(bdh, sport->port.membase + UARTBDH);
writeb(sbr & 0xFF, sport->port.membase + UARTBDL);
+ writeb(cr3, sport->port.membase + UARTCR3);
writeb(cr1, sport->port.membase + UARTCR1);
writeb(modem, sport->port.membase + UARTMODEM);
/* restore control register */
writeb(old_cr2, sport->port.membase + UARTCR2);
+ /*
+ * If new baud rate is set, we will also need to update the Ring buffer
+ * length according to the selected baud rate and restart Rx DMA path.
+ */
+ if (old) {
+ if (sport->lpuart_dma_rx_use) {
+ del_timer_sync(&sport->lpuart_timer);
+ lpuart_dma_rx_free(&sport->port);
+ }
+
+ if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
+ sport->lpuart_dma_rx_use = true;
+ rx_dma_timer_init(sport);
+ } else {
+ sport->lpuart_dma_rx_use = false;
+ }
+ }
+
spin_unlock_irqrestore(&sport->port.lock, flags);
}
@@ -1494,7 +1579,7 @@ static int lpuart_verify_port(struct uart_port *port, struct serial_struct *ser)
return ret;
}
-static struct uart_ops lpuart_pops = {
+static const struct uart_ops lpuart_pops = {
.tx_empty = lpuart_tx_empty,
.set_mctrl = lpuart_set_mctrl,
.get_mctrl = lpuart_get_mctrl,
@@ -1513,7 +1598,7 @@ static struct uart_ops lpuart_pops = {
.flush_buffer = lpuart_flush_buffer,
};
-static struct uart_ops lpuart32_pops = {
+static const struct uart_ops lpuart32_pops = {
.tx_empty = lpuart32_tx_empty,
.set_mctrl = lpuart32_set_mctrl,
.get_mctrl = lpuart32_get_mctrl,
@@ -1843,6 +1928,8 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.ops = &lpuart_pops;
sport->port.flags = UPF_BOOT_AUTOCONF;
+ sport->port.rs485_config = lpuart_config_rs485;
+
sport->clk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk)) {
ret = PTR_ERR(sport->clk);
@@ -1883,6 +1970,12 @@ static int lpuart_probe(struct platform_device *pdev)
dev_info(sport->port.dev, "DMA rx channel request failed, "
"operating without rx DMA\n");
+ if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) {
+ sport->port.rs485.flags |= SER_RS485_ENABLED;
+ sport->port.rs485.flags |= SER_RS485_RTS_ON_SEND;
+ writeb(UARTMODEM_TXRTSE, sport->port.membase + UARTMODEM);
+ }
+
return 0;
}
@@ -1923,6 +2016,32 @@ static int lpuart_suspend(struct device *dev)
uart_suspend_port(&lpuart_reg, &sport->port);
+ if (sport->lpuart_dma_rx_use) {
+ /*
+ * EDMA driver during suspend will forcefully release any
+ * non-idle DMA channels. If port wakeup is enabled or if port
+ * is console port or 'no_console_suspend' is set the Rx DMA
+ * cannot resume as as expected, hence gracefully release the
+ * Rx DMA path before suspend and start Rx DMA path on resume.
+ */
+ if (sport->port.irq_wake) {
+ del_timer_sync(&sport->lpuart_timer);
+ lpuart_dma_rx_free(&sport->port);
+ }
+
+ /* Disable Rx DMA to use UART port as wakeup source */
+ writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS,
+ sport->port.membase + UARTCR5);
+ }
+
+ if (sport->lpuart_dma_tx_use) {
+ sport->dma_tx_in_progress = false;
+ dmaengine_terminate_all(sport->dma_tx_chan);
+ }
+
+ if (sport->port.suspended && !sport->port.irq_wake)
+ clk_disable_unprepare(sport->clk);
+
return 0;
}
@@ -1931,6 +2050,9 @@ static int lpuart_resume(struct device *dev)
struct lpuart_port *sport = dev_get_drvdata(dev);
unsigned long temp;
+ if (sport->port.suspended && !sport->port.irq_wake)
+ clk_prepare_enable(sport->clk);
+
if (sport->lpuart32) {
lpuart32_setup_watermark(sport);
temp = lpuart32_read(sport->port.membase + UARTCTRL);
@@ -1944,6 +2066,26 @@ static int lpuart_resume(struct device *dev)
writeb(temp, sport->port.membase + UARTCR2);
}
+ if (sport->lpuart_dma_rx_use) {
+ if (sport->port.irq_wake) {
+ if (!lpuart_start_rx_dma(sport)) {
+ sport->lpuart_dma_rx_use = true;
+ rx_dma_timer_init(sport);
+ } else {
+ sport->lpuart_dma_rx_use = false;
+ }
+ }
+ }
+
+ if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
+ init_waitqueue_head(&sport->dma_wait);
+ sport->lpuart_dma_tx_use = true;
+ writeb(readb(sport->port.membase + UARTCR5) |
+ UARTCR5_TDMAS, sport->port.membase + UARTCR5);
+ } else {
+ sport->lpuart_dma_tx_use = false;
+ }
+
uart_resume_port(&lpuart_reg, &sport->port);
return 0;
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 0df2b1c..a70356d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -190,6 +190,7 @@
enum imx_uart_type {
IMX1_UART,
IMX21_UART,
+ IMX53_UART,
IMX6Q_UART,
};
@@ -222,6 +223,9 @@ struct imx_port {
struct dma_chan *dma_chan_rx, *dma_chan_tx;
struct scatterlist rx_sgl, tx_sgl[2];
void *rx_buf;
+ struct circ_buf rx_ring;
+ unsigned int rx_periods;
+ dma_cookie_t rx_cookie;
unsigned int tx_bytes;
unsigned int dma_tx_nents;
wait_queue_head_t dma_wait;
@@ -244,6 +248,10 @@ static struct imx_uart_data imx_uart_devdata[] = {
.uts_reg = IMX21_UTS,
.devtype = IMX21_UART,
},
+ [IMX53_UART] = {
+ .uts_reg = IMX21_UTS,
+ .devtype = IMX53_UART,
+ },
[IMX6Q_UART] = {
.uts_reg = IMX21_UTS,
.devtype = IMX6Q_UART,
@@ -258,6 +266,9 @@ static const struct platform_device_id imx_uart_devtype[] = {
.name = "imx21-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
}, {
+ .name = "imx53-uart",
+ .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX53_UART],
+ }, {
.name = "imx6q-uart",
.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART],
}, {
@@ -268,6 +279,7 @@ MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
static const struct of_device_id imx_uart_dt_ids[] = {
{ .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], },
+ { .compatible = "fsl,imx53-uart", .data = &imx_uart_devdata[IMX53_UART], },
{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
{ /* sentinel */ }
@@ -289,6 +301,11 @@ static inline int is_imx21_uart(struct imx_port *sport)
return sport->devdata->devtype == IMX21_UART;
}
+static inline int is_imx53_uart(struct imx_port *sport)
+{
+ return sport->devdata->devtype == IMX53_UART;
+}
+
static inline int is_imx6q_uart(struct imx_port *sport)
{
return sport->devdata->devtype == IMX6Q_UART;
@@ -701,6 +718,7 @@ out:
return IRQ_HANDLED;
}
+static void clear_rx_errors(struct imx_port *sport);
static int start_rx_dma(struct imx_port *sport);
/*
* If the RXFIFO is filled with some data, and then we
@@ -726,6 +744,11 @@ static void imx_dma_rxint(struct imx_port *sport)
temp &= ~(UCR2_ATEN);
writel(temp, sport->port.membase + UCR2);
+ /* disable the rx errors interrupts */
+ temp = readl(sport->port.membase + UCR4);
+ temp &= ~UCR4_OREN;
+ writel(temp, sport->port.membase + UCR4);
+
/* tell the DMA to receive the data. */
start_rx_dma(sport);
}
@@ -740,12 +763,13 @@ static unsigned int imx_get_hwmctrl(struct imx_port *sport)
{
unsigned int tmp = TIOCM_DSR;
unsigned usr1 = readl(sport->port.membase + USR1);
+ unsigned usr2 = readl(sport->port.membase + USR2);
if (usr1 & USR1_RTSS)
tmp |= TIOCM_CTS;
/* in DCE mode DCDIN is always 0 */
- if (!(usr1 & USR2_DCDIN))
+ if (!(usr2 & USR2_DCDIN))
tmp |= TIOCM_CAR;
if (sport->dte_mode)
@@ -932,30 +956,6 @@ static void imx_timeout(unsigned long data)
}
#define RX_BUF_SIZE (PAGE_SIZE)
-static void imx_rx_dma_done(struct imx_port *sport)
-{
- unsigned long temp;
- unsigned long flags;
-
- spin_lock_irqsave(&sport->port.lock, flags);
-
- /* re-enable interrupts to get notified when new symbols are incoming */
- temp = readl(sport->port.membase + UCR1);
- temp |= UCR1_RRDYEN;
- writel(temp, sport->port.membase + UCR1);
-
- temp = readl(sport->port.membase + UCR2);
- temp |= UCR2_ATEN;
- writel(temp, sport->port.membase + UCR2);
-
- sport->dma_is_rxing = 0;
-
- /* Is the shutdown waiting for us? */
- if (waitqueue_active(&sport->dma_wait))
- wake_up(&sport->dma_wait);
-
- spin_unlock_irqrestore(&sport->port.lock, flags);
-}
/*
* There are two kinds of RX DMA interrupts(such as in the MX6Q):
@@ -972,43 +972,76 @@ static void dma_rx_callback(void *data)
struct scatterlist *sgl = &sport->rx_sgl;
struct tty_port *port = &sport->port.state->port;
struct dma_tx_state state;
+ struct circ_buf *rx_ring = &sport->rx_ring;
enum dma_status status;
- unsigned int count;
-
- /* unmap it first */
- dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
+ unsigned int w_bytes = 0;
+ unsigned int r_bytes;
+ unsigned int bd_size;
status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
- count = RX_BUF_SIZE - state.residue;
- dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
+ if (status == DMA_ERROR) {
+ dev_err(sport->port.dev, "DMA transaction error.\n");
+ clear_rx_errors(sport);
+ return;
+ }
+
+ if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) {
+
+ /*
+ * The state-residue variable represents the empty space
+ * relative to the entire buffer. Taking this in consideration
+ * the head is always calculated base on the buffer total
+ * length - DMA transaction residue. The UART script from the
+ * SDMA firmware will jump to the next buffer descriptor,
+ * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4).
+ * Taking this in consideration the tail is always at the
+ * beginning of the buffer descriptor that contains the head.
+ */
+
+ /* Calculate the head */
+ rx_ring->head = sg_dma_len(sgl) - state.residue;
+
+ /* Calculate the tail. */
+ bd_size = sg_dma_len(sgl) / sport->rx_periods;
+ rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size;
+
+ if (rx_ring->head <= sg_dma_len(sgl) &&
+ rx_ring->head > rx_ring->tail) {
+
+ /* Move data from tail to head */
+ r_bytes = rx_ring->head - rx_ring->tail;
- if (count) {
- if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) {
- int bytes = tty_insert_flip_string(port, sport->rx_buf,
- count);
+ /* CPU claims ownership of RX DMA buffer */
+ dma_sync_sg_for_cpu(sport->port.dev, sgl, 1,
+ DMA_FROM_DEVICE);
- if (bytes != count)
+ w_bytes = tty_insert_flip_string(port,
+ sport->rx_buf + rx_ring->tail, r_bytes);
+
+ /* UART retrieves ownership of RX DMA buffer */
+ dma_sync_sg_for_device(sport->port.dev, sgl, 1,
+ DMA_FROM_DEVICE);
+
+ if (w_bytes != r_bytes)
sport->port.icount.buf_overrun++;
+
+ sport->port.icount.rx += w_bytes;
+ } else {
+ WARN_ON(rx_ring->head > sg_dma_len(sgl));
+ WARN_ON(rx_ring->head <= rx_ring->tail);
}
- tty_flip_buffer_push(port);
- sport->port.icount.rx += count;
}
- /*
- * Restart RX DMA directly if more data is available in order to skip
- * the roundtrip through the IRQ handler. If there is some data already
- * in the FIFO, DMA needs to be restarted soon anyways.
- *
- * Otherwise stop the DMA and reactivate FIFO IRQs to restart DMA once
- * data starts to arrive again.
- */
- if (readl(sport->port.membase + USR2) & USR2_RDR)
- start_rx_dma(sport);
- else
- imx_rx_dma_done(sport);
+ if (w_bytes) {
+ tty_flip_buffer_push(port);
+ dev_dbg(sport->port.dev, "We get %d bytes.\n", w_bytes);
+ }
}
+/* RX DMA buffer periods */
+#define RX_DMA_PERIODS 4
+
static int start_rx_dma(struct imx_port *sport)
{
struct scatterlist *sgl = &sport->rx_sgl;
@@ -1017,14 +1050,21 @@ static int start_rx_dma(struct imx_port *sport)
struct dma_async_tx_descriptor *desc;
int ret;
+ sport->rx_ring.head = 0;
+ sport->rx_ring.tail = 0;
+ sport->rx_periods = RX_DMA_PERIODS;
+
sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);
ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE);
if (ret == 0) {
dev_err(dev, "DMA mapping error for RX.\n");
return -EINVAL;
}
- desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
+
+ desc = dmaengine_prep_dma_cyclic(chan, sg_dma_address(sgl),
+ sg_dma_len(sgl), sg_dma_len(sgl) / sport->rx_periods,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+
if (!desc) {
dma_unmap_sg(dev, sgl, 1, DMA_FROM_DEVICE);
dev_err(dev, "We cannot prepare for the RX slave dma!\n");
@@ -1034,11 +1074,36 @@ static int start_rx_dma(struct imx_port *sport)
desc->callback_param = sport;
dev_dbg(dev, "RX: prepare for the DMA.\n");
- dmaengine_submit(desc);
+ sport->rx_cookie = dmaengine_submit(desc);
dma_async_issue_pending(chan);
return 0;
}
+static void clear_rx_errors(struct imx_port *sport)
+{
+ unsigned int status_usr1, status_usr2;
+
+ status_usr1 = readl(sport->port.membase + USR1);
+ status_usr2 = readl(sport->port.membase + USR2);
+
+ if (status_usr2 & USR2_BRCD) {
+ sport->port.icount.brk++;
+ writel(USR2_BRCD, sport->port.membase + USR2);
+ } else if (status_usr1 & USR1_FRAMERR) {
+ sport->port.icount.frame++;
+ writel(USR1_FRAMERR, sport->port.membase + USR1);
+ } else if (status_usr1 & USR1_PARITYERR) {
+ sport->port.icount.parity++;
+ writel(USR1_PARITYERR, sport->port.membase + USR1);
+ }
+
+ if (status_usr2 & USR2_ORE) {
+ sport->port.icount.overrun++;
+ writel(USR2_ORE, sport->port.membase + USR2);
+ }
+
+}
+
#define TXTL_DEFAULT 2 /* reset default */
#define RXTL_DEFAULT 1 /* reset default */
#define TXTL_DMA 8 /* DMA burst setting */
@@ -1058,14 +1123,16 @@ static void imx_setup_ufcr(struct imx_port *sport,
static void imx_uart_dma_exit(struct imx_port *sport)
{
if (sport->dma_chan_rx) {
+ dmaengine_terminate_sync(sport->dma_chan_rx);
dma_release_channel(sport->dma_chan_rx);
sport->dma_chan_rx = NULL;
-
+ sport->rx_cookie = -EINVAL;
kfree(sport->rx_buf);
sport->rx_buf = NULL;
}
if (sport->dma_chan_tx) {
+ dmaengine_terminate_sync(sport->dma_chan_tx);
dma_release_channel(sport->dma_chan_tx);
sport->dma_chan_tx = NULL;
}
@@ -1103,6 +1170,7 @@ static int imx_uart_dma_init(struct imx_port *sport)
ret = -ENOMEM;
goto err;
}
+ sport->rx_ring.buf = sport->rx_buf;
/* Prepare for TX : */
sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");
@@ -1201,8 +1269,7 @@ static int imx_startup(struct uart_port *port)
writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
/* Can we enable the DMA support? */
- if (is_imx6q_uart(sport) && !uart_console(port) &&
- !sport->dma_is_inited)
+ if (!uart_console(port) && !sport->dma_is_inited)
imx_uart_dma_init(sport);
spin_lock_irqsave(&sport->port.lock, flags);
@@ -1283,17 +1350,11 @@ static void imx_shutdown(struct uart_port *port)
unsigned long flags;
if (sport->dma_is_enabled) {
- int ret;
+ sport->dma_is_rxing = 0;
+ sport->dma_is_txing = 0;
+ dmaengine_terminate_sync(sport->dma_chan_tx);
+ dmaengine_terminate_sync(sport->dma_chan_rx);
- /* We have to wait for the DMA to finish. */
- ret = wait_event_interruptible(sport->dma_wait,
- !sport->dma_is_rxing && !sport->dma_is_txing);
- if (ret != 0) {
- sport->dma_is_rxing = 0;
- sport->dma_is_txing = 0;
- dmaengine_terminate_all(sport->dma_chan_tx);
- dmaengine_terminate_all(sport->dma_chan_rx);
- }
spin_lock_irqsave(&sport->port.lock, flags);
imx_stop_tx(port);
imx_stop_rx(port);
@@ -1690,7 +1751,7 @@ static int imx_rs485_config(struct uart_port *port,
return 0;
}
-static struct uart_ops imx_pops = {
+static const struct uart_ops imx_pops = {
.tx_empty = imx_tx_empty,
.set_mctrl = imx_set_mctrl,
.get_mctrl = imx_get_mctrl,
@@ -2077,8 +2138,10 @@ static int serial_imx_probe(struct platform_device *pdev)
/* For register access, we only need to enable the ipg clock. */
ret = clk_prepare_enable(sport->clk_ipg);
- if (ret)
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable per clk: %d\n", ret);
return ret;
+ }
/* Disable interrupts before requesting them */
reg = readl_relaxed(sport->port.membase + UCR1);
@@ -2095,18 +2158,26 @@ static int serial_imx_probe(struct platform_device *pdev)
if (txirq > 0) {
ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,
dev_name(&pdev->dev), sport);
- if (ret)
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request rx irq: %d\n",
+ ret);
return ret;
+ }
ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,
dev_name(&pdev->dev), sport);
- if (ret)
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request tx irq: %d\n",
+ ret);
return ret;
+ }
} else {
ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
dev_name(&pdev->dev), sport);
- if (ret)
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
return ret;
+ }
}
imx_ports[sport->port.line] = sport;
diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c
index c5ddfe5..ec7d838 100644
--- a/drivers/tty/serial/jsm/jsm_tty.c
+++ b/drivers/tty/serial/jsm/jsm_tty.c
@@ -346,7 +346,7 @@ static void jsm_config_port(struct uart_port *port, int flags)
port->type = PORT_JSM;
}
-static struct uart_ops jsm_ops = {
+static const struct uart_ops jsm_ops = {
.tx_empty = jsm_tty_tx_empty,
.set_mctrl = jsm_tty_set_mctrl,
.get_mctrl = jsm_tty_get_mctrl,
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index 5c4c280..ace8264 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -712,7 +712,7 @@ static void max3100_break_ctl(struct uart_port *port, int break_state)
dev_dbg(&s->spi->dev, "%s\n", __func__);
}
-static struct uart_ops max3100_ops = {
+static const struct uart_ops max3100_ops = {
.tx_empty = max3100_tx_empty,
.set_mctrl = max3100_set_mctrl,
.get_mctrl = max3100_get_mctrl,
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 9360801..8a3e926 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -1329,9 +1329,9 @@ static int max310x_spi_probe(struct spi_device *spi)
const struct spi_device_id *id_entry = spi_get_device_id(spi);
devtype = (struct max310x_devtype *)id_entry->driver_data;
- flags = IRQF_TRIGGER_FALLING;
}
+ flags = IRQF_TRIGGER_FALLING;
regcfg.max_register = devtype->nr * 0x20 - 1;
regmap = devm_regmap_init_spi(spi, &regcfg);
diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c
index a44290e..e72ea61 100644
--- a/drivers/tty/serial/men_z135_uart.c
+++ b/drivers/tty/serial/men_z135_uart.c
@@ -775,7 +775,7 @@ static int men_z135_verify_port(struct uart_port *port,
return -EINVAL;
}
-static struct uart_ops men_z135_ops = {
+static const struct uart_ops men_z135_ops = {
.tx_empty = men_z135_tx_empty,
.set_mctrl = men_z135_set_mctrl,
.get_mctrl = men_z135_get_mctrl,
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index eb54e5c..770454e 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1317,7 +1317,7 @@ static void mxs_auart_break_ctl(struct uart_port *u, int ctl)
mxs_clr(AUART_LINECTRL_BRK, s, REG_LINECTRL);
}
-static struct uart_ops mxs_auart_ops = {
+static const struct uart_ops mxs_auart_ops = {
.tx_empty = mxs_auart_tx_empty,
.start_tx = mxs_auart_start_tx,
.stop_tx = mxs_auart_stop_tx,
@@ -1510,10 +1510,7 @@ static int mxs_get_clks(struct mxs_auart_port *s,
if (!is_asm9260_auart(s)) {
s->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(s->clk))
- return PTR_ERR(s->clk);
-
- return 0;
+ return PTR_ERR_OR_ZERO(s->clk);
}
s->clk = devm_clk_get(s->dev, "mod");
@@ -1537,16 +1534,20 @@ static int mxs_get_clks(struct mxs_auart_port *s,
err = clk_set_rate(s->clk, clk_get_rate(s->clk_ahb));
if (err) {
dev_err(s->dev, "Failed to set rate!\n");
- return err;
+ goto disable_clk_ahb;
}
err = clk_prepare_enable(s->clk);
if (err) {
dev_err(s->dev, "Failed to enable clk!\n");
- return err;
+ goto disable_clk_ahb;
}
return 0;
+
+disable_clk_ahb:
+ clk_disable_unprepare(s->clk_ahb);
+ return err;
}
/*
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index ea4ffc2..d391650 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -31,6 +31,7 @@
#include <linux/dmi.h>
#include <linux/nmi.h>
#include <linux/delay.h>
+#include <linux/of.h>
#include <linux/debugfs.h>
#include <linux/dmaengine.h>
@@ -1603,7 +1604,7 @@ static void pch_uart_put_poll_char(struct uart_port *port,
}
#endif /* CONFIG_CONSOLE_POLL */
-static struct uart_ops pch_uart_ops = {
+static const struct uart_ops pch_uart_ops = {
.tx_empty = pch_uart_tx_empty,
.set_mctrl = pch_uart_set_mctrl,
.get_mctrl = pch_uart_get_mctrl,
@@ -1826,6 +1827,10 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
priv->trigger_level = 1;
priv->fcr = 0;
+ if (pdev->dev.of_node)
+ of_property_read_u32(pdev->dev.of_node, "clock-frequency"
+ , &user_uartclk);
+
#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
pch_uart_ports[board->line_no] = priv;
#endif
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index ae2095a..f44615f 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1577,7 +1577,7 @@ static void s3c24xx_serial_resetport(struct uart_port *port,
}
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
unsigned long val, void *data)
diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h
index 2ae4fce..a04acef 100644
--- a/drivers/tty/serial/samsung.h
+++ b/drivers/tty/serial/samsung.h
@@ -102,7 +102,7 @@ struct s3c24xx_uart_port {
struct s3c24xx_uart_dma *dma;
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
struct notifier_block freq_transition;
#endif
};
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index f36e6df..a9d94f7 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1205,6 +1205,10 @@ static int sc16is7xx_probe(struct device *dev,
}
#endif
+ /* reset device, purging any pending irq / data */
+ regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT,
+ SC16IS7XX_IOCONTROL_SRESET_BIT);
+
for (i = 0; i < devtype->nr_uart; ++i) {
s->p[i].line = i;
/* Initialize port data */
@@ -1234,6 +1238,22 @@ static int sc16is7xx_probe(struct device *dev,
init_kthread_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
/* Register port */
uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
+
+ /* Enable EFR */
+ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG,
+ SC16IS7XX_LCR_CONF_MODE_B);
+
+ regcache_cache_bypass(s->regmap, true);
+
+ /* Enable write access to enhanced features */
+ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFR_REG,
+ SC16IS7XX_EFR_ENABLE_BIT);
+
+ regcache_cache_bypass(s->regmap, false);
+
+ /* Restore access to general registers */
+ sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, 0x00);
+
/* Go to suspend mode */
sc16is7xx_power(&s->p[i].port, 0);
}
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 9fc1533..6e4f636 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -235,18 +235,9 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state,
if (tty_port_initialized(port))
return 0;
- /*
- * Set the TTY IO error marker - we will only clear this
- * once we have successfully opened the port.
- */
- set_bit(TTY_IO_ERROR, &tty->flags);
-
retval = uart_port_startup(tty, state, init_hw);
- if (!retval) {
- tty_port_set_initialized(port, 1);
- clear_bit(TTY_IO_ERROR, &tty->flags);
- } else if (retval > 0)
- retval = 0;
+ if (retval)
+ set_bit(TTY_IO_ERROR, &tty->flags);
return retval;
}
@@ -972,8 +963,11 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
}
uart_change_speed(tty, state, NULL);
}
- } else
+ } else {
retval = uart_startup(tty, state, 1);
+ if (retval > 0)
+ retval = 0;
+ }
exit:
return retval;
}
@@ -1139,6 +1133,8 @@ static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state)
uport->ops->config_port(uport, flags);
ret = uart_startup(tty, state, 1);
+ if (ret > 0)
+ ret = 0;
}
out:
mutex_unlock(&port->mutex);
@@ -1465,7 +1461,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
{
struct uart_state *state = tty->driver_data;
struct tty_port *port;
- struct uart_port *uport;
if (!state) {
struct uart_driver *drv = tty->driver->driver_state;
@@ -1481,56 +1476,36 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
port = &state->port;
pr_debug("uart_close(%d) called\n", tty->index);
- if (tty_port_close_start(port, tty, filp) == 0)
- return;
+ tty_port_close(tty->port, tty, filp);
+}
- mutex_lock(&port->mutex);
- uport = uart_port_check(state);
+static void uart_tty_port_shutdown(struct tty_port *port)
+{
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport = uart_port_check(state);
/*
* At this point, we stop accepting input. To do this, we
* disable the receive line status interrupts.
*/
- if (tty_port_initialized(port) &&
- !WARN(!uport, "detached port still initialized!\n")) {
- spin_lock_irq(&uport->lock);
- uport->ops->stop_rx(uport);
- spin_unlock_irq(&uport->lock);
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important if there is a transmit FIFO!
- */
- uart_wait_until_sent(tty, uport->timeout);
- }
-
- uart_shutdown(tty, state);
- tty_port_tty_set(port, NULL);
+ if (WARN(!uport, "detached port still initialized!\n"))
+ return;
- spin_lock_irq(&port->lock);
+ spin_lock_irq(&uport->lock);
+ uport->ops->stop_rx(uport);
+ spin_unlock_irq(&uport->lock);
- if (port->blocked_open) {
- spin_unlock_irq(&port->lock);
- if (port->close_delay)
- msleep_interruptible(jiffies_to_msecs(port->close_delay));
- spin_lock_irq(&port->lock);
- } else if (uport && !uart_console(uport)) {
- spin_unlock_irq(&port->lock);
- uart_change_pm(state, UART_PM_STATE_OFF);
- spin_lock_irq(&port->lock);
- }
- spin_unlock_irq(&port->lock);
- tty_port_set_active(port, 0);
+ uart_port_shutdown(port);
/*
- * Wake up anyone trying to open this port.
+ * It's possible for shutdown to be called after suspend if we get
+ * a DCD drop (hangup) at just the right time. Clear suspended bit so
+ * we don't try to resume a port that has been shutdown.
*/
- wake_up_interruptible(&port->open_wait);
+ tty_port_set_suspended(port, 0);
- mutex_unlock(&port->mutex);
+ uart_change_pm(state, UART_PM_STATE_OFF);
- tty_ldisc_flush(tty);
- tty->closing = 0;
}
static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
@@ -1711,52 +1686,31 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
struct uart_driver *drv = tty->driver->driver_state;
int retval, line = tty->index;
struct uart_state *state = drv->state + line;
- struct tty_port *port = &state->port;
- struct uart_port *uport;
- pr_debug("uart_open(%d) called\n", line);
+ tty->driver_data = state;
- spin_lock_irq(&port->lock);
- ++port->count;
- spin_unlock_irq(&port->lock);
+ retval = tty_port_open(&state->port, tty, filp);
+ if (retval > 0)
+ retval = 0;
- /*
- * We take the semaphore here to guarantee that we won't be re-entered
- * while allocating the state structure, or while we request any IRQs
- * that the driver may need. This also has the nice side-effect that
- * it delays the action of uart_hangup, so we can guarantee that
- * state->port.tty will always contain something reasonable.
- */
- if (mutex_lock_interruptible(&port->mutex)) {
- retval = -ERESTARTSYS;
- goto end;
- }
+ return retval;
+}
+
+static int uart_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct uart_state *state = container_of(port, struct uart_state, port);
+ struct uart_port *uport;
uport = uart_port_check(state);
- if (!uport || uport->flags & UPF_DEAD) {
- retval = -ENXIO;
- goto err_unlock;
- }
+ if (!uport || uport->flags & UPF_DEAD)
+ return -ENXIO;
- tty->driver_data = state;
- uport->state = state;
port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0;
- tty_port_tty_set(port, tty);
/*
* Start up the serial port.
*/
- retval = uart_startup(tty, state, 0);
-
- /*
- * If we succeeded, wait until the port is ready.
- */
-err_unlock:
- mutex_unlock(&port->mutex);
- if (retval == 0)
- retval = tty_port_block_til_ready(port, tty, filp);
-end:
- return retval;
+ return uart_startup(tty, state, 0);
}
static const char *uart_type(struct uart_port *port)
@@ -1940,7 +1894,7 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
*
* Returns 0 on success or -EINVAL on failure
*/
-int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
+int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr,
char **options)
{
if (strncmp(p, "mmio,", 5) == 0) {
@@ -1968,7 +1922,11 @@ int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
return -EINVAL;
}
- *addr = simple_strtoul(p, NULL, 0);
+ /*
+ * Before you replace it with kstrtoull(), think about options separator
+ * (',') it will not tolerate
+ */
+ *addr = simple_strtoull(p, NULL, 0);
p = strchr(p, ',');
if (p)
p++;
@@ -2470,6 +2428,8 @@ static const struct tty_operations uart_ops = {
static const struct tty_port_operations uart_port_ops = {
.carrier_raised = uart_carrier_raised,
.dtr_rts = uart_dtr_rts,
+ .activate = uart_port_activate,
+ .shutdown = uart_tty_port_shutdown,
};
/**
@@ -2786,6 +2746,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
uport->cons = drv->cons;
uport->minor = drv->tty_driver->minor_start + uport->line;
+ port->console = uart_console(uport);
+
/*
* If this port is a console, then the spinlock is already
* initialised.
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index d86eee3..4b26252 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -2533,7 +2533,7 @@ static int sci_verify_port(struct uart_port *port, struct serial_struct *ser)
return 0;
}
-static struct uart_ops sci_uart_ops = {
+static const struct uart_ops sci_uart_ops = {
.tx_empty = sci_tx_empty,
.set_mctrl = sci_set_mctrl,
.get_mctrl = sci_get_mctrl,
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
index 2d78cb3..379e5bd 100644
--- a/drivers/tty/serial/st-asc.c
+++ b/drivers/tty/serial/st-asc.c
@@ -639,7 +639,7 @@ static void asc_put_poll_char(struct uart_port *port, unsigned char c)
/*---------------------------------------------------------------------*/
-static struct uart_ops asc_uart_ops = {
+static const struct uart_ops asc_uart_ops = {
.tx_empty = asc_tx_empty,
.set_mctrl = asc_set_mctrl,
.get_mctrl = asc_get_mctrl,
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index f89d1f7..0338562 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) Maxime Coquelin 2015
- * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * Authors: Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * Gerald Baeza <gerald.baeza@st.com>
* License terms: GNU General Public License (GPL), version 2
*
* Inspired by st-asc.c from STMicroelectronics (c)
@@ -10,120 +11,31 @@
#define SUPPORT_SYSRQ
#endif
-#include <linux/module.h>
-#include <linux/serial.h>
+#include <linux/clk.h>
#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/dma-direction.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/irq.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/pm_runtime.h>
+#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/serial_core.h>
-#include <linux/clk.h>
-
-#define DRIVER_NAME "stm32-usart"
-
-/* Register offsets */
-#define USART_SR 0x00
-#define USART_DR 0x04
-#define USART_BRR 0x08
-#define USART_CR1 0x0c
-#define USART_CR2 0x10
-#define USART_CR3 0x14
-#define USART_GTPR 0x18
-
-/* USART_SR */
-#define USART_SR_PE BIT(0)
-#define USART_SR_FE BIT(1)
-#define USART_SR_NF BIT(2)
-#define USART_SR_ORE BIT(3)
-#define USART_SR_IDLE BIT(4)
-#define USART_SR_RXNE BIT(5)
-#define USART_SR_TC BIT(6)
-#define USART_SR_TXE BIT(7)
-#define USART_SR_LBD BIT(8)
-#define USART_SR_CTS BIT(9)
-#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \
- USART_SR_FE | USART_SR_PE)
-/* Dummy bits */
-#define USART_SR_DUMMY_RX BIT(16)
-
-/* USART_DR */
-#define USART_DR_MASK GENMASK(8, 0)
-
-/* USART_BRR */
-#define USART_BRR_DIV_F_MASK GENMASK(3, 0)
-#define USART_BRR_DIV_M_MASK GENMASK(15, 4)
-#define USART_BRR_DIV_M_SHIFT 4
-
-/* USART_CR1 */
-#define USART_CR1_SBK BIT(0)
-#define USART_CR1_RWU BIT(1)
-#define USART_CR1_RE BIT(2)
-#define USART_CR1_TE BIT(3)
-#define USART_CR1_IDLEIE BIT(4)
-#define USART_CR1_RXNEIE BIT(5)
-#define USART_CR1_TCIE BIT(6)
-#define USART_CR1_TXEIE BIT(7)
-#define USART_CR1_PEIE BIT(8)
-#define USART_CR1_PS BIT(9)
-#define USART_CR1_PCE BIT(10)
-#define USART_CR1_WAKE BIT(11)
-#define USART_CR1_M BIT(12)
-#define USART_CR1_UE BIT(13)
-#define USART_CR1_OVER8 BIT(15)
-#define USART_CR1_IE_MASK GENMASK(8, 4)
-
-/* USART_CR2 */
-#define USART_CR2_ADD_MASK GENMASK(3, 0)
-#define USART_CR2_LBDL BIT(5)
-#define USART_CR2_LBDIE BIT(6)
-#define USART_CR2_LBCL BIT(8)
-#define USART_CR2_CPHA BIT(9)
-#define USART_CR2_CPOL BIT(10)
-#define USART_CR2_CLKEN BIT(11)
-#define USART_CR2_STOP_2B BIT(13)
-#define USART_CR2_STOP_MASK GENMASK(13, 12)
-#define USART_CR2_LINEN BIT(14)
-
-/* USART_CR3 */
-#define USART_CR3_EIE BIT(0)
-#define USART_CR3_IREN BIT(1)
-#define USART_CR3_IRLP BIT(2)
-#define USART_CR3_HDSEL BIT(3)
-#define USART_CR3_NACK BIT(4)
-#define USART_CR3_SCEN BIT(5)
-#define USART_CR3_DMAR BIT(6)
-#define USART_CR3_DMAT BIT(7)
-#define USART_CR3_RTSE BIT(8)
-#define USART_CR3_CTSE BIT(9)
-#define USART_CR3_CTSIE BIT(10)
-#define USART_CR3_ONEBIT BIT(11)
-
-/* USART_GTPR */
-#define USART_GTPR_PSC_MASK GENMASK(7, 0)
-#define USART_GTPR_GT_MASK GENMASK(15, 8)
-
-#define DRIVER_NAME "stm32-usart"
-#define STM32_SERIAL_NAME "ttyS"
-#define STM32_MAX_PORTS 6
-
-struct stm32_port {
- struct uart_port port;
- struct clk *clk;
- bool hw_flow_control;
-};
+#include <linux/serial.h>
+#include <linux/spinlock.h>
+#include <linux/sysrq.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
-static struct stm32_port stm32_ports[STM32_MAX_PORTS];
-static struct uart_driver stm32_usart_driver;
+#include "stm32-usart.h"
static void stm32_stop_tx(struct uart_port *port);
+static void stm32_transmit_chars(struct uart_port *port);
static inline struct stm32_port *to_stm32_port(struct uart_port *port)
{
@@ -148,19 +60,64 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits)
writel_relaxed(val, port->membase + reg);
}
-static void stm32_receive_chars(struct uart_port *port)
+static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res,
+ bool threaded)
+{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ enum dma_status status;
+ struct dma_tx_state state;
+
+ *sr = readl_relaxed(port->membase + ofs->isr);
+
+ if (threaded && stm32_port->rx_ch) {
+ status = dmaengine_tx_status(stm32_port->rx_ch,
+ stm32_port->rx_ch->cookie,
+ &state);
+ if ((status == DMA_IN_PROGRESS) &&
+ (*last_res != state.residue))
+ return 1;
+ else
+ return 0;
+ } else if (*sr & USART_SR_RXNE) {
+ return 1;
+ }
+ return 0;
+}
+
+static unsigned long
+stm32_get_char(struct uart_port *port, u32 *sr, int *last_res)
+{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ unsigned long c;
+
+ if (stm32_port->rx_ch) {
+ c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--];
+ if ((*last_res) == 0)
+ *last_res = RX_BUF_L;
+ return c;
+ } else {
+ return readl_relaxed(port->membase + ofs->rdr);
+ }
+}
+
+static void stm32_receive_chars(struct uart_port *port, bool threaded)
{
struct tty_port *tport = &port->state->port;
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
unsigned long c;
u32 sr;
char flag;
+ static int last_res = RX_BUF_L;
if (port->irq_wake)
pm_wakeup_event(tport->tty->dev, 0);
- while ((sr = readl_relaxed(port->membase + USART_SR)) & USART_SR_RXNE) {
+ while (stm32_pending_rx(port, &sr, &last_res, threaded)) {
sr |= USART_SR_DUMMY_RX;
- c = readl_relaxed(port->membase + USART_DR);
+ c = stm32_get_char(port, &sr, &last_res);
flag = TTY_NORMAL;
port->icount.rx++;
@@ -170,6 +127,10 @@ static void stm32_receive_chars(struct uart_port *port)
if (uart_handle_break(port))
continue;
} else if (sr & USART_SR_ORE) {
+ if (ofs->icr != UNDEF_REG)
+ writel_relaxed(USART_ICR_ORECF,
+ port->membase +
+ ofs->icr);
port->icount.overrun++;
} else if (sr & USART_SR_PE) {
port->icount.parity++;
@@ -197,14 +158,138 @@ static void stm32_receive_chars(struct uart_port *port)
spin_lock(&port->lock);
}
+static void stm32_tx_dma_complete(void *arg)
+{
+ struct uart_port *port = arg;
+ struct stm32_port *stm32port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
+ unsigned int isr;
+ int ret;
+
+ ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
+ isr,
+ (isr & USART_SR_TC),
+ 10, 100000);
+
+ if (ret)
+ dev_err(port->dev, "terminal count not set\n");
+
+ if (ofs->icr == UNDEF_REG)
+ stm32_clr_bits(port, ofs->isr, USART_SR_TC);
+ else
+ stm32_set_bits(port, ofs->icr, USART_CR_TC);
+
+ stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
+ stm32port->tx_dma_busy = false;
+
+ /* Let's see if we have pending data to send */
+ stm32_transmit_chars(port);
+}
+
+static void stm32_transmit_chars_pio(struct uart_port *port)
+{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int isr;
+ int ret;
+
+ if (stm32_port->tx_dma_busy) {
+ stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
+ stm32_port->tx_dma_busy = false;
+ }
+
+ ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
+ isr,
+ (isr & USART_SR_TXE),
+ 10, 100);
+
+ if (ret)
+ dev_err(port->dev, "tx empty not set\n");
+
+ stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE);
+
+ writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+}
+
+static void stm32_transmit_chars_dma(struct uart_port *port)
+{
+ struct stm32_port *stm32port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
+ struct circ_buf *xmit = &port->state->xmit;
+ struct dma_async_tx_descriptor *desc = NULL;
+ dma_cookie_t cookie;
+ unsigned int count, i;
+
+ if (stm32port->tx_dma_busy)
+ return;
+
+ stm32port->tx_dma_busy = true;
+
+ count = uart_circ_chars_pending(xmit);
+
+ if (count > TX_BUF_L)
+ count = TX_BUF_L;
+
+ if (xmit->tail < xmit->head) {
+ memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], count);
+ } else {
+ size_t one = UART_XMIT_SIZE - xmit->tail;
+ size_t two;
+
+ if (one > count)
+ one = count;
+ two = count - one;
+
+ memcpy(&stm32port->tx_buf[0], &xmit->buf[xmit->tail], one);
+ if (two)
+ memcpy(&stm32port->tx_buf[one], &xmit->buf[0], two);
+ }
+
+ desc = dmaengine_prep_slave_single(stm32port->tx_ch,
+ stm32port->tx_dma_buf,
+ count,
+ DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT);
+
+ if (!desc) {
+ for (i = count; i > 0; i--)
+ stm32_transmit_chars_pio(port);
+ return;
+ }
+
+ desc->callback = stm32_tx_dma_complete;
+ desc->callback_param = port;
+
+ /* Push current DMA TX transaction in the pending queue */
+ cookie = dmaengine_submit(desc);
+
+ /* Issue pending DMA TX requests */
+ dma_async_issue_pending(stm32port->tx_ch);
+
+ stm32_clr_bits(port, ofs->isr, USART_SR_TC);
+ stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT);
+
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ port->icount.tx += count;
+}
+
static void stm32_transmit_chars(struct uart_port *port)
{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct circ_buf *xmit = &port->state->xmit;
if (port->x_char) {
- writel_relaxed(port->x_char, port->membase + USART_DR);
+ if (stm32_port->tx_dma_busy)
+ stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
+ writel_relaxed(port->x_char, port->membase + ofs->tdr);
port->x_char = 0;
port->icount.tx++;
+ if (stm32_port->tx_dma_busy)
+ stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT);
return;
}
@@ -218,9 +303,10 @@ static void stm32_transmit_chars(struct uart_port *port)
return;
}
- writel_relaxed(xmit->buf[xmit->tail], port->membase + USART_DR);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ if (stm32_port->tx_ch)
+ stm32_transmit_chars_dma(port);
+ else
+ stm32_transmit_chars_pio(port);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
@@ -232,34 +318,60 @@ static void stm32_transmit_chars(struct uart_port *port)
static irqreturn_t stm32_interrupt(int irq, void *ptr)
{
struct uart_port *port = ptr;
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
u32 sr;
spin_lock(&port->lock);
- sr = readl_relaxed(port->membase + USART_SR);
+ sr = readl_relaxed(port->membase + ofs->isr);
- if (sr & USART_SR_RXNE)
- stm32_receive_chars(port);
+ if ((sr & USART_SR_RXNE) && !(stm32_port->rx_ch))
+ stm32_receive_chars(port, false);
- if (sr & USART_SR_TXE)
+ if ((sr & USART_SR_TXE) && !(stm32_port->tx_ch))
stm32_transmit_chars(port);
spin_unlock(&port->lock);
+ if (stm32_port->rx_ch)
+ return IRQ_WAKE_THREAD;
+ else
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stm32_threaded_interrupt(int irq, void *ptr)
+{
+ struct uart_port *port = ptr;
+ struct stm32_port *stm32_port = to_stm32_port(port);
+
+ spin_lock(&port->lock);
+
+ if (stm32_port->rx_ch)
+ stm32_receive_chars(port, true);
+
+ spin_unlock(&port->lock);
+
return IRQ_HANDLED;
}
static unsigned int stm32_tx_empty(struct uart_port *port)
{
- return readl_relaxed(port->membase + USART_SR) & USART_SR_TXE;
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+ return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE;
}
static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
- stm32_set_bits(port, USART_CR3, USART_CR3_RTSE);
+ stm32_set_bits(port, ofs->cr3, USART_CR3_RTSE);
else
- stm32_clr_bits(port, USART_CR3, USART_CR3_RTSE);
+ stm32_clr_bits(port, ofs->cr3, USART_CR3_RTSE);
}
static unsigned int stm32_get_mctrl(struct uart_port *port)
@@ -271,7 +383,10 @@ static unsigned int stm32_get_mctrl(struct uart_port *port)
/* Transmit stop */
static void stm32_stop_tx(struct uart_port *port)
{
- stm32_clr_bits(port, USART_CR1, USART_CR1_TXEIE);
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+ stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE);
}
/* There are probably characters waiting to be transmitted. */
@@ -282,33 +397,40 @@ static void stm32_start_tx(struct uart_port *port)
if (uart_circ_empty(xmit))
return;
- stm32_set_bits(port, USART_CR1, USART_CR1_TXEIE | USART_CR1_TE);
+ stm32_transmit_chars(port);
}
/* Throttle the remote when input buffer is about to overflow. */
static void stm32_throttle(struct uart_port *port)
{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
- stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE);
+ stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE);
spin_unlock_irqrestore(&port->lock, flags);
}
/* Unthrottle the remote, the input buffer can now accept data. */
static void stm32_unthrottle(struct uart_port *port)
{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
- stm32_set_bits(port, USART_CR1, USART_CR1_RXNEIE);
+ stm32_set_bits(port, ofs->cr1, USART_CR1_RXNEIE);
spin_unlock_irqrestore(&port->lock, flags);
}
/* Receive stop */
static void stm32_stop_rx(struct uart_port *port)
{
- stm32_clr_bits(port, USART_CR1, USART_CR1_RXNEIE);
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+ stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE);
}
/* Handle breaks - ignored by us */
@@ -318,26 +440,34 @@ static void stm32_break_ctl(struct uart_port *port, int break_state)
static int stm32_startup(struct uart_port *port)
{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
const char *name = to_platform_device(port->dev)->name;
u32 val;
int ret;
- ret = request_irq(port->irq, stm32_interrupt, 0, name, port);
+ ret = request_threaded_irq(port->irq, stm32_interrupt,
+ stm32_threaded_interrupt,
+ IRQF_NO_SUSPEND, name, port);
if (ret)
return ret;
val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
- stm32_set_bits(port, USART_CR1, val);
+ stm32_set_bits(port, ofs->cr1, val);
return 0;
}
static void stm32_shutdown(struct uart_port *port)
{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ struct stm32_usart_config *cfg = &stm32_port->info->cfg;
u32 val;
val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
- stm32_set_bits(port, USART_CR1, val);
+ val |= BIT(cfg->uart_enable_bit);
+ stm32_clr_bits(port, ofs->cr1, val);
free_irq(port->irq, port);
}
@@ -346,6 +476,8 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ struct stm32_usart_config *cfg = &stm32_port->info->cfg;
unsigned int baud;
u32 usartdiv, mantissa, fraction, oversampling;
tcflag_t cflag = termios->c_cflag;
@@ -360,9 +492,10 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
spin_lock_irqsave(&port->lock, flags);
/* Stop serial port and reset value */
- writel_relaxed(0, port->membase + USART_CR1);
+ writel_relaxed(0, port->membase + ofs->cr1);
- cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE | USART_CR1_RXNEIE;
+ cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
+ cr1 |= BIT(cfg->uart_enable_bit);
cr2 = 0;
cr3 = 0;
@@ -371,8 +504,12 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
if (cflag & PARENB) {
cr1 |= USART_CR1_PCE;
- if ((cflag & CSIZE) == CS8)
- cr1 |= USART_CR1_M;
+ if ((cflag & CSIZE) == CS8) {
+ if (cfg->has_7bits_data)
+ cr1 |= USART_CR1_M0;
+ else
+ cr1 |= USART_CR1_M;
+ }
}
if (cflag & PARODD)
@@ -394,15 +531,15 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
*/
if (usartdiv < 16) {
oversampling = 8;
- stm32_set_bits(port, USART_CR1, USART_CR1_OVER8);
+ stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8);
} else {
oversampling = 16;
- stm32_clr_bits(port, USART_CR1, USART_CR1_OVER8);
+ stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
}
mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT;
fraction = usartdiv % oversampling;
- writel_relaxed(mantissa | fraction, port->membase + USART_BRR);
+ writel_relaxed(mantissa | fraction, port->membase + ofs->brr);
uart_update_timeout(port, cflag, baud);
@@ -430,9 +567,12 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= USART_SR_DUMMY_RX;
- writel_relaxed(cr3, port->membase + USART_CR3);
- writel_relaxed(cr2, port->membase + USART_CR2);
- writel_relaxed(cr1, port->membase + USART_CR1);
+ if (stm32_port->rx_ch)
+ cr3 |= USART_CR3_DMAR;
+
+ writel_relaxed(cr3, port->membase + ofs->cr3);
+ writel_relaxed(cr2, port->membase + ofs->cr2);
+ writel_relaxed(cr1, port->membase + ofs->cr1);
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -469,6 +609,8 @@ static void stm32_pm(struct uart_port *port, unsigned int state,
{
struct stm32_port *stm32port = container_of(port,
struct stm32_port, port);
+ struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
+ struct stm32_usart_config *cfg = &stm32port->info->cfg;
unsigned long flags = 0;
switch (state) {
@@ -477,7 +619,7 @@ static void stm32_pm(struct uart_port *port, unsigned int state,
break;
case UART_PM_STATE_OFF:
spin_lock_irqsave(&port->lock, flags);
- stm32_clr_bits(port, USART_CR1, USART_CR1_UE);
+ stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
spin_unlock_irqrestore(&port->lock, flags);
clk_disable_unprepare(stm32port->clk);
break;
@@ -539,8 +681,6 @@ static int stm32_init_port(struct stm32_port *stm32port,
if (!stm32port->port.uartclk)
ret = -EINVAL;
- clk_disable_unprepare(stm32port->clk);
-
return ret;
}
@@ -560,30 +700,162 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev)
return NULL;
stm32_ports[id].hw_flow_control = of_property_read_bool(np,
- "auto-flow-control");
+ "st,hw-flow-ctrl");
stm32_ports[id].port.line = id;
return &stm32_ports[id];
}
#ifdef CONFIG_OF
static const struct of_device_id stm32_match[] = {
- { .compatible = "st,stm32-usart", },
- { .compatible = "st,stm32-uart", },
+ { .compatible = "st,stm32-usart", .data = &stm32f4_info},
+ { .compatible = "st,stm32-uart", .data = &stm32f4_info},
+ { .compatible = "st,stm32f7-usart", .data = &stm32f7_info},
+ { .compatible = "st,stm32f7-uart", .data = &stm32f7_info},
{},
};
MODULE_DEVICE_TABLE(of, stm32_match);
#endif
-static int stm32_serial_probe(struct platform_device *pdev)
+static int stm32_of_dma_rx_probe(struct stm32_port *stm32port,
+ struct platform_device *pdev)
{
+ struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
+ struct uart_port *port = &stm32port->port;
+ struct device *dev = &pdev->dev;
+ struct dma_slave_config config;
+ struct dma_async_tx_descriptor *desc = NULL;
+ dma_cookie_t cookie;
int ret;
+
+ /* Request DMA RX channel */
+ stm32port->rx_ch = dma_request_slave_channel(dev, "rx");
+ if (!stm32port->rx_ch) {
+ dev_info(dev, "rx dma alloc failed\n");
+ return -ENODEV;
+ }
+ stm32port->rx_buf = dma_alloc_coherent(&pdev->dev, RX_BUF_L,
+ &stm32port->rx_dma_buf,
+ GFP_KERNEL);
+ if (!stm32port->rx_buf) {
+ ret = -ENOMEM;
+ goto alloc_err;
+ }
+
+ /* Configure DMA channel */
+ memset(&config, 0, sizeof(config));
+ config.src_addr = port->mapbase + ofs->rdr;
+ config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+ ret = dmaengine_slave_config(stm32port->rx_ch, &config);
+ if (ret < 0) {
+ dev_err(dev, "rx dma channel config failed\n");
+ ret = -ENODEV;
+ goto config_err;
+ }
+
+ /* Prepare a DMA cyclic transaction */
+ desc = dmaengine_prep_dma_cyclic(stm32port->rx_ch,
+ stm32port->rx_dma_buf,
+ RX_BUF_L, RX_BUF_P, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(dev, "rx dma prep cyclic failed\n");
+ ret = -ENODEV;
+ goto config_err;
+ }
+
+ /* No callback as dma buffer is drained on usart interrupt */
+ desc->callback = NULL;
+ desc->callback_param = NULL;
+
+ /* Push current DMA transaction in the pending queue */
+ cookie = dmaengine_submit(desc);
+
+ /* Issue pending DMA requests */
+ dma_async_issue_pending(stm32port->rx_ch);
+
+ return 0;
+
+config_err:
+ dma_free_coherent(&pdev->dev,
+ RX_BUF_L, stm32port->rx_buf,
+ stm32port->rx_dma_buf);
+
+alloc_err:
+ dma_release_channel(stm32port->rx_ch);
+ stm32port->rx_ch = NULL;
+
+ return ret;
+}
+
+static int stm32_of_dma_tx_probe(struct stm32_port *stm32port,
+ struct platform_device *pdev)
+{
+ struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
+ struct uart_port *port = &stm32port->port;
+ struct device *dev = &pdev->dev;
+ struct dma_slave_config config;
+ int ret;
+
+ stm32port->tx_dma_busy = false;
+
+ /* Request DMA TX channel */
+ stm32port->tx_ch = dma_request_slave_channel(dev, "tx");
+ if (!stm32port->tx_ch) {
+ dev_info(dev, "tx dma alloc failed\n");
+ return -ENODEV;
+ }
+ stm32port->tx_buf = dma_alloc_coherent(&pdev->dev, TX_BUF_L,
+ &stm32port->tx_dma_buf,
+ GFP_KERNEL);
+ if (!stm32port->tx_buf) {
+ ret = -ENOMEM;
+ goto alloc_err;
+ }
+
+ /* Configure DMA channel */
+ memset(&config, 0, sizeof(config));
+ config.dst_addr = port->mapbase + ofs->tdr;
+ config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+ ret = dmaengine_slave_config(stm32port->tx_ch, &config);
+ if (ret < 0) {
+ dev_err(dev, "tx dma channel config failed\n");
+ ret = -ENODEV;
+ goto config_err;
+ }
+
+ return 0;
+
+config_err:
+ dma_free_coherent(&pdev->dev,
+ TX_BUF_L, stm32port->tx_buf,
+ stm32port->tx_dma_buf);
+
+alloc_err:
+ dma_release_channel(stm32port->tx_ch);
+ stm32port->tx_ch = NULL;
+
+ return ret;
+}
+
+static int stm32_serial_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
struct stm32_port *stm32port;
+ int ret;
stm32port = stm32_of_get_stm32_port(pdev);
if (!stm32port)
return -ENODEV;
+ match = of_match_device(stm32_match, &pdev->dev);
+ if (match && match->data)
+ stm32port->info = (struct stm32_usart_info *)match->data;
+ else
+ return -EINVAL;
+
ret = stm32_init_port(stm32port, pdev);
if (ret)
return ret;
@@ -592,6 +864,14 @@ static int stm32_serial_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = stm32_of_dma_rx_probe(stm32port, pdev);
+ if (ret)
+ dev_info(&pdev->dev, "interrupt mode used for rx (no dma)\n");
+
+ ret = stm32_of_dma_tx_probe(stm32port, pdev);
+ if (ret)
+ dev_info(&pdev->dev, "interrupt mode used for tx (no dma)\n");
+
platform_set_drvdata(pdev, &stm32port->port);
return 0;
@@ -600,6 +880,30 @@ static int stm32_serial_probe(struct platform_device *pdev)
static int stm32_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+ stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
+
+ if (stm32_port->rx_ch)
+ dma_release_channel(stm32_port->rx_ch);
+
+ if (stm32_port->rx_dma_buf)
+ dma_free_coherent(&pdev->dev,
+ RX_BUF_L, stm32_port->rx_buf,
+ stm32_port->rx_dma_buf);
+
+ stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
+
+ if (stm32_port->tx_ch)
+ dma_release_channel(stm32_port->tx_ch);
+
+ if (stm32_port->tx_dma_buf)
+ dma_free_coherent(&pdev->dev,
+ TX_BUF_L, stm32_port->tx_buf,
+ stm32_port->tx_dma_buf);
+
+ clk_disable_unprepare(stm32_port->clk);
return uart_remove_one_port(&stm32_usart_driver, port);
}
@@ -608,15 +912,21 @@ static int stm32_serial_remove(struct platform_device *pdev)
#ifdef CONFIG_SERIAL_STM32_CONSOLE
static void stm32_console_putchar(struct uart_port *port, int ch)
{
- while (!(readl_relaxed(port->membase + USART_SR) & USART_SR_TXE))
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+ while (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE))
cpu_relax();
- writel_relaxed(ch, port->membase + USART_DR);
+ writel_relaxed(ch, port->membase + ofs->tdr);
}
static void stm32_console_write(struct console *co, const char *s, unsigned cnt)
{
struct uart_port *port = &stm32_ports[co->index].port;
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+ struct stm32_usart_config *cfg = &stm32_port->info->cfg;
unsigned long flags;
u32 old_cr1, new_cr1;
int locked = 1;
@@ -629,15 +939,16 @@ static void stm32_console_write(struct console *co, const char *s, unsigned cnt)
else
spin_lock(&port->lock);
- /* Save and disable interrupts */
- old_cr1 = readl_relaxed(port->membase + USART_CR1);
+ /* Save and disable interrupts, enable the transmitter */
+ old_cr1 = readl_relaxed(port->membase + ofs->cr1);
new_cr1 = old_cr1 & ~USART_CR1_IE_MASK;
- writel_relaxed(new_cr1, port->membase + USART_CR1);
+ new_cr1 |= USART_CR1_TE | BIT(cfg->uart_enable_bit);
+ writel_relaxed(new_cr1, port->membase + ofs->cr1);
uart_console_write(port, s, cnt, stm32_console_putchar);
/* Restore interrupt state */
- writel_relaxed(old_cr1, port->membase + USART_CR1);
+ writel_relaxed(old_cr1, port->membase + ofs->cr1);
if (locked)
spin_unlock(&port->lock);
diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
new file mode 100644
index 0000000..41d9749
--- /dev/null
+++ b/drivers/tty/serial/stm32-usart.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) Maxime Coquelin 2015
+ * Authors: Maxime Coquelin <mcoquelin.stm32@gmail.com>
+ * Gerald Baeza <gerald_baeza@yahoo.fr>
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#define DRIVER_NAME "stm32-usart"
+
+struct stm32_usart_offsets {
+ u8 cr1;
+ u8 cr2;
+ u8 cr3;
+ u8 brr;
+ u8 gtpr;
+ u8 rtor;
+ u8 rqr;
+ u8 isr;
+ u8 icr;
+ u8 rdr;
+ u8 tdr;
+};
+
+struct stm32_usart_config {
+ u8 uart_enable_bit; /* USART_CR1_UE */
+ bool has_7bits_data;
+};
+
+struct stm32_usart_info {
+ struct stm32_usart_offsets ofs;
+ struct stm32_usart_config cfg;
+};
+
+#define UNDEF_REG ~0
+
+/* Register offsets */
+struct stm32_usart_info stm32f4_info = {
+ .ofs = {
+ .isr = 0x00,
+ .rdr = 0x04,
+ .tdr = 0x04,
+ .brr = 0x08,
+ .cr1 = 0x0c,
+ .cr2 = 0x10,
+ .cr3 = 0x14,
+ .gtpr = 0x18,
+ .rtor = UNDEF_REG,
+ .rqr = UNDEF_REG,
+ .icr = UNDEF_REG,
+ },
+ .cfg = {
+ .uart_enable_bit = 13,
+ .has_7bits_data = false,
+ }
+};
+
+struct stm32_usart_info stm32f7_info = {
+ .ofs = {
+ .cr1 = 0x00,
+ .cr2 = 0x04,
+ .cr3 = 0x08,
+ .brr = 0x0c,
+ .gtpr = 0x10,
+ .rtor = 0x14,
+ .rqr = 0x18,
+ .isr = 0x1c,
+ .icr = 0x20,
+ .rdr = 0x24,
+ .tdr = 0x28,
+ },
+ .cfg = {
+ .uart_enable_bit = 0,
+ .has_7bits_data = true,
+ }
+};
+
+/* USART_SR (F4) / USART_ISR (F7) */
+#define USART_SR_PE BIT(0)
+#define USART_SR_FE BIT(1)
+#define USART_SR_NF BIT(2)
+#define USART_SR_ORE BIT(3)
+#define USART_SR_IDLE BIT(4)
+#define USART_SR_RXNE BIT(5)
+#define USART_SR_TC BIT(6)
+#define USART_SR_TXE BIT(7)
+#define USART_SR_LBD BIT(8)
+#define USART_SR_CTSIF BIT(9)
+#define USART_SR_CTS BIT(10) /* F7 */
+#define USART_SR_RTOF BIT(11) /* F7 */
+#define USART_SR_EOBF BIT(12) /* F7 */
+#define USART_SR_ABRE BIT(14) /* F7 */
+#define USART_SR_ABRF BIT(15) /* F7 */
+#define USART_SR_BUSY BIT(16) /* F7 */
+#define USART_SR_CMF BIT(17) /* F7 */
+#define USART_SR_SBKF BIT(18) /* F7 */
+#define USART_SR_TEACK BIT(21) /* F7 */
+#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \
+ USART_SR_FE | USART_SR_PE)
+/* Dummy bits */
+#define USART_SR_DUMMY_RX BIT(16)
+
+/* USART_ICR (F7) */
+#define USART_CR_TC BIT(6)
+
+/* USART_DR */
+#define USART_DR_MASK GENMASK(8, 0)
+
+/* USART_BRR */
+#define USART_BRR_DIV_F_MASK GENMASK(3, 0)
+#define USART_BRR_DIV_M_MASK GENMASK(15, 4)
+#define USART_BRR_DIV_M_SHIFT 4
+
+/* USART_CR1 */
+#define USART_CR1_SBK BIT(0)
+#define USART_CR1_RWU BIT(1) /* F4 */
+#define USART_CR1_RE BIT(2)
+#define USART_CR1_TE BIT(3)
+#define USART_CR1_IDLEIE BIT(4)
+#define USART_CR1_RXNEIE BIT(5)
+#define USART_CR1_TCIE BIT(6)
+#define USART_CR1_TXEIE BIT(7)
+#define USART_CR1_PEIE BIT(8)
+#define USART_CR1_PS BIT(9)
+#define USART_CR1_PCE BIT(10)
+#define USART_CR1_WAKE BIT(11)
+#define USART_CR1_M BIT(12)
+#define USART_CR1_M0 BIT(12) /* F7 */
+#define USART_CR1_MME BIT(13) /* F7 */
+#define USART_CR1_CMIE BIT(14) /* F7 */
+#define USART_CR1_OVER8 BIT(15)
+#define USART_CR1_DEDT_MASK GENMASK(20, 16) /* F7 */
+#define USART_CR1_DEAT_MASK GENMASK(25, 21) /* F7 */
+#define USART_CR1_RTOIE BIT(26) /* F7 */
+#define USART_CR1_EOBIE BIT(27) /* F7 */
+#define USART_CR1_M1 BIT(28) /* F7 */
+#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27))
+
+/* USART_CR2 */
+#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */
+#define USART_CR2_ADDM7 BIT(4) /* F7 */
+#define USART_CR2_LBDL BIT(5)
+#define USART_CR2_LBDIE BIT(6)
+#define USART_CR2_LBCL BIT(8)
+#define USART_CR2_CPHA BIT(9)
+#define USART_CR2_CPOL BIT(10)
+#define USART_CR2_CLKEN BIT(11)
+#define USART_CR2_STOP_2B BIT(13)
+#define USART_CR2_STOP_MASK GENMASK(13, 12)
+#define USART_CR2_LINEN BIT(14)
+#define USART_CR2_SWAP BIT(15) /* F7 */
+#define USART_CR2_RXINV BIT(16) /* F7 */
+#define USART_CR2_TXINV BIT(17) /* F7 */
+#define USART_CR2_DATAINV BIT(18) /* F7 */
+#define USART_CR2_MSBFIRST BIT(19) /* F7 */
+#define USART_CR2_ABREN BIT(20) /* F7 */
+#define USART_CR2_ABRMOD_MASK GENMASK(22, 21) /* F7 */
+#define USART_CR2_RTOEN BIT(23) /* F7 */
+#define USART_CR2_ADD_F7_MASK GENMASK(31, 24) /* F7 */
+
+/* USART_CR3 */
+#define USART_CR3_EIE BIT(0)
+#define USART_CR3_IREN BIT(1)
+#define USART_CR3_IRLP BIT(2)
+#define USART_CR3_HDSEL BIT(3)
+#define USART_CR3_NACK BIT(4)
+#define USART_CR3_SCEN BIT(5)
+#define USART_CR3_DMAR BIT(6)
+#define USART_CR3_DMAT BIT(7)
+#define USART_CR3_RTSE BIT(8)
+#define USART_CR3_CTSE BIT(9)
+#define USART_CR3_CTSIE BIT(10)
+#define USART_CR3_ONEBIT BIT(11)
+#define USART_CR3_OVRDIS BIT(12) /* F7 */
+#define USART_CR3_DDRE BIT(13) /* F7 */
+#define USART_CR3_DEM BIT(14) /* F7 */
+#define USART_CR3_DEP BIT(15) /* F7 */
+#define USART_CR3_SCARCNT_MASK GENMASK(19, 17) /* F7 */
+
+/* USART_GTPR */
+#define USART_GTPR_PSC_MASK GENMASK(7, 0)
+#define USART_GTPR_GT_MASK GENMASK(15, 8)
+
+/* USART_RTOR */
+#define USART_RTOR_RTO_MASK GENMASK(23, 0) /* F7 */
+#define USART_RTOR_BLEN_MASK GENMASK(31, 24) /* F7 */
+
+/* USART_RQR */
+#define USART_RQR_ABRRQ BIT(0) /* F7 */
+#define USART_RQR_SBKRQ BIT(1) /* F7 */
+#define USART_RQR_MMRQ BIT(2) /* F7 */
+#define USART_RQR_RXFRQ BIT(3) /* F7 */
+#define USART_RQR_TXFRQ BIT(4) /* F7 */
+
+/* USART_ICR */
+#define USART_ICR_PECF BIT(0) /* F7 */
+#define USART_ICR_FFECF BIT(1) /* F7 */
+#define USART_ICR_NCF BIT(2) /* F7 */
+#define USART_ICR_ORECF BIT(3) /* F7 */
+#define USART_ICR_IDLECF BIT(4) /* F7 */
+#define USART_ICR_TCCF BIT(6) /* F7 */
+#define USART_ICR_LBDCF BIT(8) /* F7 */
+#define USART_ICR_CTSCF BIT(9) /* F7 */
+#define USART_ICR_RTOCF BIT(11) /* F7 */
+#define USART_ICR_EOBCF BIT(12) /* F7 */
+#define USART_ICR_CMCF BIT(17) /* F7 */
+
+#define STM32_SERIAL_NAME "ttyS"
+#define STM32_MAX_PORTS 6
+
+#define RX_BUF_L 200 /* dma rx buffer length */
+#define RX_BUF_P RX_BUF_L /* dma rx buffer period */
+#define TX_BUF_L 200 /* dma tx buffer length */
+
+struct stm32_port {
+ struct uart_port port;
+ struct clk *clk;
+ struct stm32_usart_info *info;
+ struct dma_chan *rx_ch; /* dma rx channel */
+ dma_addr_t rx_dma_buf; /* dma rx buffer bus address */
+ unsigned char *rx_buf; /* dma rx buffer cpu address */
+ struct dma_chan *tx_ch; /* dma tx channel */
+ dma_addr_t tx_dma_buf; /* dma tx buffer bus address */
+ unsigned char *tx_buf; /* dma tx buffer cpu address */
+ bool tx_dma_busy; /* dma tx busy */
+ bool hw_flow_control;
+};
+
+static struct stm32_port stm32_ports[STM32_MAX_PORTS];
+static struct uart_driver stm32_usart_driver;
diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c
index 512c162..5da7fe4 100644
--- a/drivers/tty/serial/timbuart.c
+++ b/drivers/tty/serial/timbuart.c
@@ -394,7 +394,7 @@ static int timbuart_verify_port(struct uart_port *port,
return -EINVAL;
}
-static struct uart_ops timbuart_ops = {
+static const struct uart_ops timbuart_ops = {
.tx_empty = timbuart_tx_empty,
.set_mctrl = timbuart_set_mctrl,
.get_mctrl = timbuart_get_mctrl,
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 05089b6..817bb0d 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -387,7 +387,7 @@ static void ulite_put_poll_char(struct uart_port *port, unsigned char ch)
}
#endif
-static struct uart_ops ulite_ops = {
+static const struct uart_ops ulite_ops = {
.tx_empty = ulite_tx_empty,
.set_mctrl = ulite_set_mctrl,
.get_mctrl = ulite_get_mctrl,
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 23cfc5e..6b85adc 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -118,7 +118,7 @@ struct vt8500_port {
* have been allocated as we can't use pdev->id in
* devicetree
*/
-static unsigned long vt8500_ports_in_use;
+static DECLARE_BITMAP(vt8500_ports_in_use, VT8500_MAX_PORTS);
static inline void vt8500_write(struct uart_port *port, unsigned int val,
unsigned int off)
@@ -663,15 +663,15 @@ static int vt8500_serial_probe(struct platform_device *pdev)
if (port < 0) {
/* calculate the port id */
- port = find_first_zero_bit(&vt8500_ports_in_use,
- sizeof(vt8500_ports_in_use));
+ port = find_first_zero_bit(vt8500_ports_in_use,
+ VT8500_MAX_PORTS);
}
if (port >= VT8500_MAX_PORTS)
return -ENODEV;
/* reserve the port id */
- if (test_and_set_bit(port, &vt8500_ports_in_use)) {
+ if (test_and_set_bit(port, vt8500_ports_in_use)) {
/* port already in use - shouldn't really happen */
return -EBUSY;
}
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 9ca1a4d..f37edaa 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -57,7 +57,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
#define CDNS_UART_IMR 0x10 /* Interrupt Mask */
#define CDNS_UART_ISR 0x14 /* Interrupt Status */
#define CDNS_UART_BAUDGEN 0x18 /* Baud Rate Generator */
-#define CDNS_UART_RXTOUT 0x1C /* RX Timeout */
+#define CDNS_UART_RXTOUT 0x1C /* RX Timeout */
#define CDNS_UART_RXWM 0x20 /* RX FIFO Trigger Level */
#define CDNS_UART_MODEMCR 0x24 /* Modem Control */
#define CDNS_UART_MODEMSR 0x28 /* Modem Status */
@@ -68,6 +68,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
#define CDNS_UART_IRRX_PWIDTH 0x3C /* IR Min Received Pulse Width */
#define CDNS_UART_IRTX_PWIDTH 0x40 /* IR Transmitted pulse Width */
#define CDNS_UART_TXWM 0x44 /* TX FIFO Trigger Level */
+#define CDNS_UART_RXBS 0x48 /* RX FIFO byte status register */
/* Control Register Bit Definitions */
#define CDNS_UART_CR_STOPBRK 0x00000100 /* Stop TX break */
@@ -79,6 +80,9 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
#define CDNS_UART_CR_TXRST 0x00000002 /* TX logic reset */
#define CDNS_UART_CR_RXRST 0x00000001 /* RX logic reset */
#define CDNS_UART_CR_RST_TO 0x00000040 /* Restart Timeout Counter */
+#define CDNS_UART_RXBS_PARITY 0x00000001 /* Parity error status */
+#define CDNS_UART_RXBS_FRAMING 0x00000002 /* Framing error status */
+#define CDNS_UART_RXBS_BRK 0x00000004 /* Overrun error status */
/*
* Mode Register:
@@ -126,13 +130,27 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
#define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */
#define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */
-#define CDNS_UART_RX_IRQS (CDNS_UART_IXR_PARITY | CDNS_UART_IXR_FRAMING | \
- CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_RXTRIG | \
+ /*
+ * Do not enable parity error interrupt for the following
+ * reason: When parity error interrupt is enabled, each Rx
+ * parity error always results in 2 events. The first one
+ * being parity error interrupt and the second one with a
+ * proper Rx interrupt with the incoming data. Disabling
+ * parity error interrupt ensures better handling of parity
+ * error events. With this change, for a parity error case, we
+ * get a Rx interrupt with parity error set in ISR register
+ * and we still handle parity errors in the desired way.
+ */
+
+#define CDNS_UART_RX_IRQS (CDNS_UART_IXR_FRAMING | \
+ CDNS_UART_IXR_OVERRUN | \
+ CDNS_UART_IXR_RXTRIG | \
CDNS_UART_IXR_TOUT)
/* Goes in read_status_mask for break detection as the HW doesn't do it*/
-#define CDNS_UART_IXR_BRK 0x80000000
+#define CDNS_UART_IXR_BRK 0x00002000
+#define CDNS_UART_RXBS_SUPPORT BIT(1)
/*
* Modem Control register:
* The read/write Modem Control register controls the interface with the modem
@@ -172,46 +190,66 @@ struct cdns_uart {
struct clk *pclk;
unsigned int baud;
struct notifier_block clk_rate_change_nb;
+ u32 quirks;
+};
+struct cdns_platform_data {
+ u32 quirks;
};
#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \
clk_rate_change_nb);
-static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus)
+/**
+ * cdns_uart_handle_rx - Handle the received bytes along with Rx errors.
+ * @dev_id: Id of the UART port
+ * @isrstatus: The interrupt status register value as read
+ * Return: None
+ */
+static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus)
{
- /*
- * There is no hardware break detection, so we interpret framing
- * error with all-zeros data as a break sequence. Most of the time,
- * there's another non-zero byte at the end of the sequence.
- */
- if (isrstatus & CDNS_UART_IXR_FRAMING) {
- while (!(readl(port->membase + CDNS_UART_SR) &
- CDNS_UART_SR_RXEMPTY)) {
- if (!readl(port->membase + CDNS_UART_FIFO)) {
+ struct uart_port *port = (struct uart_port *)dev_id;
+ struct cdns_uart *cdns_uart = port->private_data;
+ unsigned int data;
+ unsigned int rxbs_status = 0;
+ unsigned int status_mask;
+ unsigned int framerrprocessed = 0;
+ char status = TTY_NORMAL;
+ bool is_rxbs_support;
+
+ is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT;
+
+ while ((readl(port->membase + CDNS_UART_SR) &
+ CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {
+ if (is_rxbs_support)
+ rxbs_status = readl(port->membase + CDNS_UART_RXBS);
+ data = readl(port->membase + CDNS_UART_FIFO);
+ port->icount.rx++;
+ /*
+ * There is no hardware break detection in Zynq, so we interpret
+ * framing error with all-zeros data as a break sequence.
+ * Most of the time, there's another non-zero byte at the
+ * end of the sequence.
+ */
+ if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) {
+ if (!data) {
port->read_status_mask |= CDNS_UART_IXR_BRK;
- isrstatus &= ~CDNS_UART_IXR_FRAMING;
+ framerrprocessed = 1;
+ continue;
}
}
- writel(CDNS_UART_IXR_FRAMING, port->membase + CDNS_UART_ISR);
- }
-
- /* drop byte with parity error if IGNPAR specified */
- if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY)
- isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT);
-
- isrstatus &= port->read_status_mask;
- isrstatus &= ~port->ignore_status_mask;
-
- if (!(isrstatus & (CDNS_UART_IXR_TOUT | CDNS_UART_IXR_RXTRIG)))
- return;
-
- while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) {
- u32 data;
- char status = TTY_NORMAL;
+ if (is_rxbs_support && (rxbs_status & CDNS_UART_RXBS_BRK)) {
+ port->icount.brk++;
+ status = TTY_BREAK;
+ if (uart_handle_break(port))
+ continue;
+ }
- data = readl(port->membase + CDNS_UART_FIFO);
+ isrstatus &= port->read_status_mask;
+ isrstatus &= ~port->ignore_status_mask;
+ status_mask = port->read_status_mask;
+ status_mask &= ~port->ignore_status_mask;
- /* Non-NULL byte after BREAK is garbage (99%) */
- if (data && (port->read_status_mask & CDNS_UART_IXR_BRK)) {
+ if (data &&
+ (port->read_status_mask & CDNS_UART_IXR_BRK)) {
port->read_status_mask &= ~CDNS_UART_IXR_BRK;
port->icount.brk++;
if (uart_handle_break(port))
@@ -221,57 +259,83 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus)
if (uart_handle_sysrq_char(port, data))
continue;
- port->icount.rx++;
-
- if (isrstatus & CDNS_UART_IXR_PARITY) {
- port->icount.parity++;
- status = TTY_PARITY;
- } else if (isrstatus & CDNS_UART_IXR_FRAMING) {
- port->icount.frame++;
- status = TTY_FRAME;
- } else if (isrstatus & CDNS_UART_IXR_OVERRUN) {
+ if (is_rxbs_support) {
+ if ((rxbs_status & CDNS_UART_RXBS_PARITY)
+ && (status_mask & CDNS_UART_IXR_PARITY)) {
+ port->icount.parity++;
+ status = TTY_PARITY;
+ }
+ if ((rxbs_status & CDNS_UART_RXBS_FRAMING)
+ && (status_mask & CDNS_UART_IXR_PARITY)) {
+ port->icount.frame++;
+ status = TTY_FRAME;
+ }
+ } else {
+ if (isrstatus & CDNS_UART_IXR_PARITY) {
+ port->icount.parity++;
+ status = TTY_PARITY;
+ }
+ if ((isrstatus & CDNS_UART_IXR_FRAMING) &&
+ !framerrprocessed) {
+ port->icount.frame++;
+ status = TTY_FRAME;
+ }
+ }
+ if (isrstatus & CDNS_UART_IXR_OVERRUN) {
port->icount.overrun++;
+ tty_insert_flip_char(&port->state->port, 0,
+ TTY_OVERRUN);
}
-
- uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN,
- data, status);
+ tty_insert_flip_char(&port->state->port, data, status);
+ isrstatus = 0;
}
+ spin_unlock(&port->lock);
tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
}
-static void cdns_uart_handle_tx(struct uart_port *port)
+/**
+ * cdns_uart_handle_tx - Handle the bytes to be Txed.
+ * @dev_id: Id of the UART port
+ * Return: None
+ */
+static void cdns_uart_handle_tx(void *dev_id)
{
+ struct uart_port *port = (struct uart_port *)dev_id;
unsigned int numbytes;
if (uart_circ_empty(&port->state->xmit)) {
writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR);
- return;
- }
-
- numbytes = port->fifosize;
- while (numbytes && !uart_circ_empty(&port->state->xmit) &&
- !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
- /*
- * Get the data from the UART circular buffer
- * and write it to the cdns_uart's TX_FIFO
- * register.
- */
- writel(port->state->xmit.buf[port->state->xmit.tail],
- port->membase + CDNS_UART_FIFO);
- port->icount.tx++;
-
- /*
- * Adjust the tail of the UART buffer and wrap
- * the buffer if it reaches limit.
- */
- port->state->xmit.tail =
- (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
+ } else {
+ numbytes = port->fifosize;
+ while (numbytes && !uart_circ_empty(&port->state->xmit) &&
+ !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
+ /*
+ * Get the data from the UART circular buffer
+ * and write it to the cdns_uart's TX_FIFO
+ * register.
+ */
+ writel(
+ port->state->xmit.buf[port->state->xmit.
+ tail], port->membase + CDNS_UART_FIFO);
+
+ port->icount.tx++;
+
+ /*
+ * Adjust the tail of the UART buffer and wrap
+ * the buffer if it reaches limit.
+ */
+ port->state->xmit.tail =
+ (port->state->xmit.tail + 1) &
+ (UART_XMIT_SIZE - 1);
+
+ numbytes--;
+ }
- numbytes--;
+ if (uart_circ_chars_pending(
+ &port->state->xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
}
-
- if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
}
/**
@@ -284,27 +348,24 @@ static void cdns_uart_handle_tx(struct uart_port *port)
static irqreturn_t cdns_uart_isr(int irq, void *dev_id)
{
struct uart_port *port = (struct uart_port *)dev_id;
- unsigned long flags;
unsigned int isrstatus;
- spin_lock_irqsave(&port->lock, flags);
+ spin_lock(&port->lock);
/* Read the interrupt status register to determine which
- * interrupt(s) is/are active.
+ * interrupt(s) is/are active and clear them.
*/
isrstatus = readl(port->membase + CDNS_UART_ISR);
-
- if (isrstatus & CDNS_UART_RX_IRQS)
- cdns_uart_handle_rx(port, isrstatus);
-
- if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY)
- cdns_uart_handle_tx(port);
-
writel(isrstatus, port->membase + CDNS_UART_ISR);
- /* be sure to release the lock and tty before leaving */
- spin_unlock_irqrestore(&port->lock, flags);
+ if (isrstatus & CDNS_UART_IXR_TXEMPTY) {
+ cdns_uart_handle_tx(dev_id);
+ isrstatus &= ~CDNS_UART_IXR_TXEMPTY;
+ }
+ if (isrstatus & CDNS_UART_IXR_MASK)
+ cdns_uart_handle_rx(dev_id, isrstatus);
+ spin_unlock(&port->lock);
return IRQ_HANDLED;
}
@@ -653,6 +714,10 @@ static void cdns_uart_set_termios(struct uart_port *port,
ctrl_reg |= CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST;
writel(ctrl_reg, port->membase + CDNS_UART_CR);
+ while (readl(port->membase + CDNS_UART_CR) &
+ (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST))
+ cpu_relax();
+
/*
* Clear the RX disable and TX disable bits and then set the TX enable
* bit and RX enable bit to enable the transmitter and receiver.
@@ -736,10 +801,14 @@ static void cdns_uart_set_termios(struct uart_port *port,
*/
static int cdns_uart_startup(struct uart_port *port)
{
+ struct cdns_uart *cdns_uart = port->private_data;
+ bool is_brk_support;
int ret;
unsigned long flags;
unsigned int status = 0;
+ is_brk_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT;
+
spin_lock_irqsave(&port->lock, flags);
/* Disable the TX and RX */
@@ -752,6 +821,10 @@ static int cdns_uart_startup(struct uart_port *port)
writel(CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST,
port->membase + CDNS_UART_CR);
+ while (readl(port->membase + CDNS_UART_CR) &
+ (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST))
+ cpu_relax();
+
/*
* Clear the RX disable bit and then set the RX enable bit to enable
* the receiver.
@@ -794,7 +867,11 @@ static int cdns_uart_startup(struct uart_port *port)
}
/* Set the Interrupt Registers with desired interrupts */
- writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER);
+ if (is_brk_support)
+ writel(CDNS_UART_RX_IRQS | CDNS_UART_IXR_BRK,
+ port->membase + CDNS_UART_IER);
+ else
+ writel(CDNS_UART_RX_IRQS, port->membase + CDNS_UART_IER);
return 0;
}
@@ -993,7 +1070,7 @@ static void cdns_uart_pm(struct uart_port *port, unsigned int state,
}
}
-static struct uart_ops cdns_uart_ops = {
+static const struct uart_ops cdns_uart_ops = {
.set_mctrl = cdns_uart_set_mctrl,
.get_mctrl = cdns_uart_get_mctrl,
.start_tx = cdns_uart_start_tx,
@@ -1088,9 +1165,34 @@ static void __init cdns_early_write(struct console *con, const char *s,
static int __init cdns_early_console_setup(struct earlycon_device *device,
const char *opt)
{
- if (!device->port.membase)
+ struct uart_port *port = &device->port;
+
+ if (!port->membase)
return -ENODEV;
+ /* initialise control register */
+ writel(CDNS_UART_CR_TX_EN|CDNS_UART_CR_TXRST|CDNS_UART_CR_RXRST,
+ port->membase + CDNS_UART_CR);
+
+ /* only set baud if specified on command line - otherwise
+ * assume it has been initialized by a boot loader.
+ */
+ if (device->baud) {
+ u32 cd = 0, bdiv = 0;
+ u32 mr;
+ int div8;
+
+ cdns_uart_calc_baud_divs(port->uartclk, device->baud,
+ &bdiv, &cd, &div8);
+ mr = CDNS_UART_MR_PARITY_NONE;
+ if (div8)
+ mr |= CDNS_UART_MR_CLKSEL;
+
+ writel(mr, port->membase + CDNS_UART_MR);
+ writel(cd, port->membase + CDNS_UART_BAUDGEN);
+ writel(bdiv, port->membase + CDNS_UART_BAUDDIV);
+ }
+
device->con->write = cdns_early_write;
return 0;
@@ -1328,6 +1430,18 @@ static int cdns_uart_resume(struct device *device)
static SIMPLE_DEV_PM_OPS(cdns_uart_dev_pm_ops, cdns_uart_suspend,
cdns_uart_resume);
+static const struct cdns_platform_data zynqmp_uart_def = {
+ .quirks = CDNS_UART_RXBS_SUPPORT, };
+
+/* Match table for of_platform binding */
+static const struct of_device_id cdns_uart_of_match[] = {
+ { .compatible = "xlnx,xuartps", },
+ { .compatible = "cdns,uart-r1p8", },
+ { .compatible = "cdns,uart-r1p12", .data = &zynqmp_uart_def },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
+
/**
* cdns_uart_probe - Platform driver probe
* @pdev: Pointer to the platform device structure
@@ -1340,12 +1454,20 @@ static int cdns_uart_probe(struct platform_device *pdev)
struct uart_port *port;
struct resource *res;
struct cdns_uart *cdns_uart_data;
+ const struct of_device_id *match;
cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data),
GFP_KERNEL);
if (!cdns_uart_data)
return -ENOMEM;
+ match = of_match_node(cdns_uart_of_match, pdev->dev.of_node);
+ if (match && match->data) {
+ const struct cdns_platform_data *data = match->data;
+
+ cdns_uart_data->quirks = data->quirks;
+ }
+
cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(cdns_uart_data->pclk)) {
cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk");
@@ -1471,14 +1593,6 @@ static int cdns_uart_remove(struct platform_device *pdev)
return rc;
}
-/* Match table for of_platform binding */
-static const struct of_device_id cdns_uart_of_match[] = {
- { .compatible = "xlnx,xuartps", },
- { .compatible = "cdns,uart-r1p8", },
- {}
-};
-MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
-
static struct platform_driver cdns_uart_platform_driver = {
.probe = cdns_uart_probe,
.remove = cdns_uart_remove,
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 2705ca9..e841a4e 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1312,12 +1312,12 @@ static int vc_t416_color(struct vc_data *vc, int i,
if (i > vc->vc_npar)
return i;
- if (vc->vc_par[i] == 5 && i < vc->vc_npar) {
- /* 256 colours -- ubiquitous */
+ if (vc->vc_par[i] == 5 && i + 1 <= vc->vc_npar) {
+ /* 256 colours */
i++;
rgb_from_256(vc->vc_par[i], &c);
- } else if (vc->vc_par[i] == 2 && i <= vc->vc_npar + 3) {
- /* 24 bit -- extremely rare */
+ } else if (vc->vc_par[i] == 2 && i + 3 <= vc->vc_npar) {
+ /* 24 bit */
c.r = vc->vc_par[i + 1];
c.g = vc->vc_par[i + 2];
c.b = vc->vc_par[i + 3];
@@ -1415,6 +1415,11 @@ static void csi_m(struct vc_data *vc)
(vc->vc_color & 0x0f);
break;
default:
+ if (vc->vc_par[i] >= 90 && vc->vc_par[i] <= 107) {
+ if (vc->vc_par[i] < 100)
+ vc->vc_intensity = 2;
+ vc->vc_par[i] -= 60;
+ }
if (vc->vc_par[i] >= 30 && vc->vc_par[i] <= 37)
vc->vc_color = color_table[vc->vc_par[i] - 30]
| (vc->vc_color & 0xf0);
diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
index 915facb..e1134a4 100644
--- a/drivers/uio/uio_dmem_genirq.c
+++ b/drivers/uio/uio_dmem_genirq.c
@@ -229,7 +229,7 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
++uiomem;
}
- priv->dmem_region_start = i;
+ priv->dmem_region_start = uiomem - &uioinfo->mem[0];
priv->num_dmem_regions = pdata->num_dynamic_regions;
for (i = 0; i < pdata->num_dynamic_regions; ++i) {
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8689dcb..644e978 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -152,7 +152,8 @@ source "drivers/usb/gadget/Kconfig"
config USB_LED_TRIG
bool "USB LED Triggers"
- depends on LEDS_CLASS && USB_COMMON && LEDS_TRIGGERS
+ depends on LEDS_CLASS && LEDS_TRIGGERS
+ select USB_COMMON
help
This option adds LED triggers for USB host and/or gadget activity.
@@ -160,4 +161,25 @@ config USB_LED_TRIG
LEDs and you want to use them as activity indicators for USB host or
gadget.
+config USB_ULPI_BUS
+ tristate "USB ULPI PHY interface support"
+ select USB_COMMON
+ help
+ UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
+ USB 2.0 PHY interface. The ULPI specification defines a standard set
+ of registers that can be used to detect the vendor and product which
+ allows ULPI to be handled as a bus. This module is the driver for that
+ bus.
+
+ The ULPI interfaces (the buses) are registered by the drivers for USB
+ controllers which support ULPI register access and have ULPI PHY
+ attached to them. The ULPI PHY drivers themselves are normal PHY
+ drivers.
+
+ ULPI PHYs provide often functions such as ADP sensing/probing (OTG
+ protocol) and USB charger detection.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ulpi.
+
endif # USB_SUPPORT
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 0a866e9..f9fe86b6 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -1139,10 +1139,8 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
/* instance init */
instance = kzalloc(sizeof(*instance), GFP_KERNEL);
- if (!instance) {
- usb_dbg(usbatm_instance, "cxacru_bind: no memory for instance data\n");
+ if (!instance)
return -ENOMEM;
- }
instance->usbatm = usbatm_instance;
instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
@@ -1168,13 +1166,11 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
}
instance->rcv_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!instance->rcv_urb) {
- usb_dbg(usbatm_instance, "cxacru_bind: no memory for rcv_urb\n");
ret = -ENOMEM;
goto fail;
}
instance->snd_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!instance->snd_urb) {
- usb_dbg(usbatm_instance, "cxacru_bind: no memory for snd_urb\n");
ret = -ENOMEM;
goto fail;
}
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
index 0270d13..5083eb5 100644
--- a/drivers/usb/atm/speedtch.c
+++ b/drivers/usb/atm/speedtch.c
@@ -817,7 +817,6 @@ static int speedtch_bind(struct usbatm_data *usbatm,
instance = kzalloc(sizeof(*instance), GFP_KERNEL);
if (!instance) {
- usb_err(usbatm, "%s: no memory for instance data!\n", __func__);
ret = -ENOMEM;
goto fail_release;
}
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 4333dc5..df67815 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -2196,17 +2196,12 @@ static int uea_boot(struct uea_softc *sc)
load_XILINX_firmware(sc);
intr = kmalloc(size, GFP_KERNEL);
- if (!intr) {
- uea_err(INS_TO_USBDEV(sc),
- "cannot allocate interrupt package\n");
+ if (!intr)
goto err0;
- }
sc->urb_int = usb_alloc_urb(0, GFP_KERNEL);
- if (!sc->urb_int) {
- uea_err(INS_TO_USBDEV(sc), "cannot allocate interrupt URB\n");
+ if (!sc->urb_int)
goto err1;
- }
usb_fill_int_urb(sc->urb_int, sc->usb_dev,
usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE),
@@ -2561,10 +2556,8 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
}
sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL);
- if (!sc) {
- uea_err(usb, "uea_init: not enough memory !\n");
+ if (!sc)
return -ENOMEM;
- }
sc->usb_dev = usb;
usbatm->driver_data = sc;
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index db322d9..4dec9df 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -819,7 +819,6 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL);
if (!new) {
- atm_err(instance, "%s: no memory for vcc_data!\n", __func__);
ret = -ENOMEM;
goto fail;
}
@@ -1032,10 +1031,8 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
/* instance init */
instance = kzalloc(sizeof(*instance) + sizeof(struct urb *) * (num_rcv_urbs + num_snd_urbs), GFP_KERNEL);
- if (!instance) {
- dev_err(dev, "%s: no memory for instance data!\n", __func__);
+ if (!instance)
return -ENOMEM;
- }
/* public fields */
@@ -1141,7 +1138,6 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
urb = usb_alloc_urb(iso_packets, GFP_KERNEL);
if (!urb) {
- dev_err(dev, "%s: no memory for urb %d!\n", __func__, i);
error = -ENOMEM;
goto fail_unbind;
}
@@ -1151,7 +1147,6 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
/* zero the tx padding to avoid leaking information */
buffer = kzalloc(channel->buf_size, GFP_KERNEL);
if (!buffer) {
- dev_err(dev, "%s: no memory for buffer %d!\n", __func__, i);
error = -ENOMEM;
goto fail_unbind;
}
@@ -1182,7 +1177,6 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
instance->cell_buf = kmalloc(instance->rx_channel.stride, GFP_KERNEL);
if (!instance->cell_buf) {
- dev_err(dev, "%s: no memory for cell buffer!\n", __func__);
error = -ENOMEM;
goto fail_unbind;
}
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index dedc33e..0991794 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -140,6 +140,9 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
if (of_find_property(np, "disable-over-current", NULL))
data->disable_oc = 1;
+ if (of_find_property(np, "over-current-active-high", NULL))
+ data->oc_polarity = 1;
+
if (of_find_property(np, "external-vbus-divider", NULL))
data->evdo = 1;
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h
index 635717e..409aa5ca8 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.h
+++ b/drivers/usb/chipidea/ci_hdrc_imx.h
@@ -17,6 +17,7 @@ struct imx_usbmisc_data {
int index;
unsigned int disable_oc:1; /* over current detect disabled */
+ unsigned int oc_polarity:1; /* over current polarity if oc enabled */
unsigned int evdo:1; /* set external vbus divider option */
};
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 053bac9..96ae695 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -81,12 +81,15 @@ static int ehci_ci_reset(struct usb_hcd *hcd)
{
struct device *dev = hcd->self.controller;
struct ci_hdrc *ci = dev_get_drvdata(dev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int ret;
ret = ehci_setup(hcd);
if (ret)
return ret;
+ ehci->need_io_watchdog = 0;
+
ci_platform_configure(ci);
return ret;
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index b933568..661f43f 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -59,7 +59,7 @@ ctrl_endpt_in_desc = {
*/
static inline int hw_ep_bit(int num, int dir)
{
- return num + (dir ? 16 : 0);
+ return num + ((dir == TX) ? 16 : 0);
}
static inline int ep_to_bit(struct ci_hdrc *ci, int n)
@@ -121,9 +121,8 @@ static int hw_ep_flush(struct ci_hdrc *ci, int num, int dir)
*/
static int hw_ep_disable(struct ci_hdrc *ci, int num, int dir)
{
- hw_ep_flush(ci, num, dir);
hw_write(ci, OP_ENDPTCTRL + num,
- dir ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0);
+ (dir == TX) ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0);
return 0;
}
@@ -139,7 +138,7 @@ static int hw_ep_enable(struct ci_hdrc *ci, int num, int dir, int type)
{
u32 mask, data;
- if (dir) {
+ if (dir == TX) {
mask = ENDPTCTRL_TXT; /* type */
data = type << __ffs(mask);
@@ -171,7 +170,7 @@ static int hw_ep_enable(struct ci_hdrc *ci, int num, int dir, int type)
*/
static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir)
{
- u32 mask = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
+ u32 mask = (dir == TX) ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0;
}
@@ -188,6 +187,9 @@ static int hw_ep_prime(struct ci_hdrc *ci, int num, int dir, int is_ctrl)
{
int n = hw_ep_bit(num, dir);
+ /* Synchronize before ep prime */
+ wmb();
+
if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num)))
return -EAGAIN;
@@ -218,8 +220,8 @@ static int hw_ep_set_halt(struct ci_hdrc *ci, int num, int dir, int value)
do {
enum ci_hw_regs reg = OP_ENDPTCTRL + num;
- u32 mask_xs = dir ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
- u32 mask_xr = dir ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
+ u32 mask_xs = (dir == TX) ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
+ u32 mask_xr = (dir == TX) ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
/* data toggle - reserved for EP0 but it's in ESS */
hw_write(ci, reg, mask_xs|mask_xr,
@@ -348,8 +350,7 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
if (node == NULL)
return -ENOMEM;
- node->ptr = dma_pool_zalloc(hwep->td_pool, GFP_ATOMIC,
- &node->dma);
+ node->ptr = dma_pool_zalloc(hwep->td_pool, GFP_ATOMIC, &node->dma);
if (node->ptr == NULL) {
kfree(node);
return -ENOMEM;
@@ -506,8 +507,6 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
hwep->qh.ptr->cap |= mul << __ffs(QH_MULT);
}
- wmb(); /* synchronize before ep prime */
-
ret = hw_ep_prime(ci, hwep->num, hwep->dir,
hwep->type == USB_ENDPOINT_XFER_CONTROL);
done:
@@ -534,9 +533,6 @@ static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep,
hwep->qh.ptr->td.token &=
cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE));
- /* Synchronize before ep prime */
- wmb();
-
return hw_ep_prime(ci, hwep->num, hwep->dir,
hwep->type == USB_ENDPOINT_XFER_CONTROL);
}
@@ -590,7 +586,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
}
if (remaining_length) {
- if (hwep->dir) {
+ if (hwep->dir == TX) {
hwreq->req.status = -EPROTO;
break;
}
@@ -1051,9 +1047,9 @@ __acquires(ci->lock)
if (req.wLength != 0)
break;
num = le16_to_cpu(req.wIndex);
- dir = num & USB_ENDPOINT_DIR_MASK;
+ dir = (num & USB_ENDPOINT_DIR_MASK) ? TX : RX;
num &= USB_ENDPOINT_NUMBER_MASK;
- if (dir) /* TX */
+ if (dir == TX)
num += ci->hw_ep_max / 2;
if (!ci->ci_hw_ep[num].wedge) {
spin_unlock(&ci->lock);
@@ -1103,9 +1099,9 @@ __acquires(ci->lock)
if (req.wLength != 0)
break;
num = le16_to_cpu(req.wIndex);
- dir = num & USB_ENDPOINT_DIR_MASK;
+ dir = (num & USB_ENDPOINT_DIR_MASK) ? TX : RX;
num &= USB_ENDPOINT_NUMBER_MASK;
- if (dir) /* TX */
+ if (dir == TX)
num += ci->hw_ep_max / 2;
spin_unlock(&ci->lock);
@@ -1680,12 +1676,10 @@ static int init_eps(struct ci_hdrc *ci)
usb_ep_set_maxpacket_limit(&hwep->ep, (unsigned short)~0);
INIT_LIST_HEAD(&hwep->qh.queue);
- hwep->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL,
- &hwep->qh.dma);
+ hwep->qh.ptr = dma_pool_zalloc(ci->qh_pool, GFP_KERNEL,
+ &hwep->qh.dma);
if (hwep->qh.ptr == NULL)
retval = -ENOMEM;
- else
- memset(hwep->qh.ptr, 0, sizeof(*hwep->qh.ptr));
/*
* set up shorthands for ep0 out and in endpoints,
@@ -1999,7 +1993,7 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci)
if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC))
return -ENXIO;
- rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
+ rdrv = devm_kzalloc(ci->dev, sizeof(*rdrv), GFP_KERNEL);
if (!rdrv)
return -ENOMEM;
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index ab8b027..20d02a5 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -56,6 +56,7 @@
#define MX6_BM_NON_BURST_SETTING BIT(1)
#define MX6_BM_OVER_CUR_DIS BIT(7)
+#define MX6_BM_OVER_CUR_POLARITY BIT(8)
#define MX6_BM_WAKEUP_ENABLE BIT(10)
#define MX6_BM_ID_WAKEUP BIT(16)
#define MX6_BM_VBUS_WAKEUP BIT(17)
@@ -266,11 +267,14 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
spin_lock_irqsave(&usbmisc->lock, flags);
+ reg = readl(usbmisc->base + data->index * 4);
if (data->disable_oc) {
- reg = readl(usbmisc->base + data->index * 4);
- writel(reg | MX6_BM_OVER_CUR_DIS,
- usbmisc->base + data->index * 4);
+ reg |= MX6_BM_OVER_CUR_DIS;
+ } else if (data->oc_polarity == 1) {
+ /* High active */
+ reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
}
+ writel(reg, usbmisc->base + data->index * 4);
/* SoC non-burst setting */
reg = readl(usbmisc->base + data->index * 4);
@@ -365,10 +369,14 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
return -EINVAL;
spin_lock_irqsave(&usbmisc->lock, flags);
+ reg = readl(usbmisc->base);
if (data->disable_oc) {
- reg = readl(usbmisc->base);
- writel(reg | MX6_BM_OVER_CUR_DIS, usbmisc->base);
+ reg |= MX6_BM_OVER_CUR_DIS;
+ } else if (data->oc_polarity == 1) {
+ /* High active */
+ reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
}
+ writel(reg, usbmisc->base);
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
@@ -492,6 +500,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
.compatible = "fsl,imx6ul-usbmisc",
.data = &imx6sx_usbmisc_ops,
},
+ {
+ .compatible = "fsl,imx7d-usbmisc",
+ .data = &imx7d_usbmisc_ops,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 0f3f62e..78f0f85 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -368,17 +368,17 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
if (!test_and_clear_bit(index, &acm->read_urbs_free))
return 0;
- dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
-
res = usb_submit_urb(acm->read_urbs[index], mem_flags);
if (res) {
if (res != -EPERM) {
dev_err(&acm->data->dev,
- "%s - usb_submit_urb failed: %d\n",
- __func__, res);
+ "urb %d failed submission with %d\n",
+ index, res);
}
set_bit(index, &acm->read_urbs_free);
return res;
+ } else {
+ dev_vdbg(&acm->data->dev, "submitted urb %d\n", index);
}
return 0;
@@ -415,8 +415,9 @@ static void acm_read_bulk_callback(struct urb *urb)
unsigned long flags;
int status = urb->status;
- dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
- rb->index, urb->actual_length);
+ dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
+ rb->index, urb->actual_length,
+ status);
if (!acm->dev) {
set_bit(rb->index, &acm->read_urbs_free);
@@ -426,8 +427,6 @@ static void acm_read_bulk_callback(struct urb *urb)
if (status) {
set_bit(rb->index, &acm->read_urbs_free);
- dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
- __func__, status);
if ((status != -ENOENT) || (urb->actual_length == 0))
return;
}
@@ -462,8 +461,7 @@ static void acm_write_bulk(struct urb *urb)
int status = urb->status;
if (status || (urb->actual_length != urb->transfer_buffer_length))
- dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
- __func__,
+ dev_vdbg(&acm->data->dev, "wrote len %d/%d, status %d\n",
urb->actual_length,
urb->transfer_buffer_length,
status);
@@ -478,8 +476,6 @@ static void acm_softint(struct work_struct *work)
{
struct acm *acm = container_of(work, struct acm, work);
- dev_vdbg(&acm->data->dev, "%s\n", __func__);
-
tty_port_tty_wakeup(&acm->port);
}
@@ -492,8 +488,6 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
struct acm *acm;
int retval;
- dev_dbg(tty->dev, "%s\n", __func__);
-
acm = acm_get_by_minor(tty->index);
if (!acm)
return -ENODEV;
@@ -515,8 +509,6 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
{
struct acm *acm = tty->driver_data;
- dev_dbg(tty->dev, "%s\n", __func__);
-
return tty_port_open(&acm->port, tty, filp);
}
@@ -545,8 +537,6 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
int retval = -ENODEV;
int i;
- dev_dbg(&acm->control->dev, "%s\n", __func__);
-
mutex_lock(&acm->mutex);
if (acm->disconnected)
goto disconnected;
@@ -607,8 +597,6 @@ static void acm_port_destruct(struct tty_port *port)
{
struct acm *acm = container_of(port, struct acm, port);
- dev_dbg(&acm->control->dev, "%s\n", __func__);
-
acm_release_minor(acm);
usb_put_intf(acm->control);
kfree(acm->country_codes);
@@ -622,8 +610,6 @@ static void acm_port_shutdown(struct tty_port *port)
struct acm_wb *wb;
int i;
- dev_dbg(&acm->control->dev, "%s\n", __func__);
-
/*
* Need to grab write_lock to prevent race with resume, but no need to
* hold it due to the tty-port initialised flag.
@@ -654,21 +640,21 @@ static void acm_port_shutdown(struct tty_port *port)
static void acm_tty_cleanup(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
- dev_dbg(&acm->control->dev, "%s\n", __func__);
+
tty_port_put(&acm->port);
}
static void acm_tty_hangup(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
- dev_dbg(&acm->control->dev, "%s\n", __func__);
+
tty_port_hangup(&acm->port);
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct acm *acm = tty->driver_data;
- dev_dbg(&acm->control->dev, "%s\n", __func__);
+
tty_port_close(&acm->port, tty, filp);
}
@@ -684,7 +670,7 @@ static int acm_tty_write(struct tty_struct *tty,
if (!count)
return 0;
- dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
+ dev_vdbg(&acm->data->dev, "%d bytes from tty layer\n", count);
spin_lock_irqsave(&acm->write_lock, flags);
wbn = acm_wb_alloc(acm);
@@ -701,7 +687,7 @@ static int acm_tty_write(struct tty_struct *tty,
}
count = (count > acm->writesize) ? acm->writesize : count;
- dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
+ dev_vdbg(&acm->data->dev, "writing %d bytes\n", count);
memcpy(wb->buf, buf, count);
wb->len = count;
@@ -1193,6 +1179,9 @@ static int acm_probe(struct usb_interface *intf,
return -EINVAL;
}
+ if (!intf->cur_altsetting)
+ return -EINVAL;
+
if (!buflen) {
if (intf->cur_altsetting->endpoint &&
intf->cur_altsetting->endpoint->extralen &&
@@ -1246,6 +1235,8 @@ static int acm_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "no interfaces\n");
return -ENODEV;
}
+ if (!data_interface->cur_altsetting || !control_interface->cur_altsetting)
+ return -ENODEV;
if (data_intf_num != call_intf_num)
dev_dbg(&intf->dev, "Separate call control interface. That is not fully supported.\n");
@@ -1533,8 +1524,6 @@ static void stop_data_traffic(struct acm *acm)
{
int i;
- dev_dbg(&acm->control->dev, "%s\n", __func__);
-
usb_kill_urb(acm->ctrlurb);
for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
@@ -1551,8 +1540,6 @@ static void acm_disconnect(struct usb_interface *intf)
struct tty_struct *tty;
int i;
- dev_dbg(&intf->dev, "%s\n", __func__);
-
/* sibling interface is already cleaning up */
if (!acm)
return;
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 337948c..0a63695 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -58,6 +58,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_SUSPENDING 8
#define WDM_RESETTING 9
#define WDM_OVERFLOW 10
+#define WDM_DRAIN_ON_OPEN 11
#define WDM_MAX 16
@@ -154,6 +155,9 @@ static void wdm_out_callback(struct urb *urb)
wake_up(&desc->wait);
}
+/* forward declaration */
+static int service_outstanding_interrupt(struct wdm_device *desc);
+
static void wdm_in_callback(struct urb *urb)
{
struct wdm_device *desc = urb->context;
@@ -167,18 +171,18 @@ static void wdm_in_callback(struct urb *urb)
switch (status) {
case -ENOENT:
dev_dbg(&desc->intf->dev,
- "nonzero urb status received: -ENOENT");
+ "nonzero urb status received: -ENOENT\n");
goto skip_error;
case -ECONNRESET:
dev_dbg(&desc->intf->dev,
- "nonzero urb status received: -ECONNRESET");
+ "nonzero urb status received: -ECONNRESET\n");
goto skip_error;
case -ESHUTDOWN:
dev_dbg(&desc->intf->dev,
- "nonzero urb status received: -ESHUTDOWN");
+ "nonzero urb status received: -ESHUTDOWN\n");
goto skip_error;
case -EPIPE:
- dev_err(&desc->intf->dev,
+ dev_dbg(&desc->intf->dev,
"nonzero urb status received: -EPIPE\n");
break;
default:
@@ -188,7 +192,13 @@ static void wdm_in_callback(struct urb *urb)
}
}
- desc->rerr = status;
+ /*
+ * only set a new error if there is no previous error.
+ * Errors are only cleared during read/open
+ */
+ if (desc->rerr == 0)
+ desc->rerr = status;
+
if (length + desc->length > desc->wMaxCommand) {
/* The buffer would overflow */
set_bit(WDM_OVERFLOW, &desc->flags);
@@ -200,10 +210,40 @@ static void wdm_in_callback(struct urb *urb)
desc->reslength = length;
}
}
+
+ /*
+ * Handling devices with the WDM_DRAIN_ON_OPEN flag set:
+ * If desc->resp_count is unset, then the urb was submitted
+ * without a prior notification. If the device returned any
+ * data, then this implies that it had messages queued without
+ * notifying us. Continue reading until that queue is flushed.
+ */
+ if (!desc->resp_count) {
+ if (!length) {
+ /* do not propagate the expected -EPIPE */
+ desc->rerr = 0;
+ goto unlock;
+ }
+ dev_dbg(&desc->intf->dev, "got %d bytes without notification\n", length);
+ set_bit(WDM_RESPONDING, &desc->flags);
+ usb_submit_urb(desc->response, GFP_ATOMIC);
+ }
+
skip_error:
+ set_bit(WDM_READ, &desc->flags);
wake_up(&desc->wait);
- set_bit(WDM_READ, &desc->flags);
+ if (desc->rerr) {
+ /*
+ * Since there was an error, userspace may decide to not read
+ * any data after poll'ing.
+ * We should respond to further attempts from the device to send
+ * data, so that we can get unstuck.
+ */
+ service_outstanding_interrupt(desc);
+ }
+
+unlock:
spin_unlock(&desc->iuspin);
}
@@ -244,18 +284,18 @@ static void wdm_int_callback(struct urb *urb)
switch (dr->bNotificationType) {
case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
dev_dbg(&desc->intf->dev,
- "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d",
+ "NOTIFY_RESPONSE_AVAILABLE received: index %d len %d\n",
le16_to_cpu(dr->wIndex), le16_to_cpu(dr->wLength));
break;
case USB_CDC_NOTIFY_NETWORK_CONNECTION:
dev_dbg(&desc->intf->dev,
- "NOTIFY_NETWORK_CONNECTION %s network",
+ "NOTIFY_NETWORK_CONNECTION %s network\n",
dr->wValue ? "connected to" : "disconnected from");
goto exit;
case USB_CDC_NOTIFY_SPEED_CHANGE:
- dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)",
+ dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)\n",
urb->actual_length);
goto exit;
default:
@@ -274,8 +314,7 @@ static void wdm_int_callback(struct urb *urb)
&& !test_bit(WDM_DISCONNECTING, &desc->flags)
&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
rv = usb_submit_urb(desc->response, GFP_ATOMIC);
- dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
- __func__, rv);
+ dev_dbg(&desc->intf->dev, "submit response URB %d\n", rv);
}
spin_unlock(&desc->iuspin);
if (rv < 0) {
@@ -417,7 +456,7 @@ static ssize_t wdm_write
rv = usb_translate_errors(rv);
goto out_free_mem_pm;
} else {
- dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
+ dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d\n",
le16_to_cpu(req->wIndex));
}
@@ -436,17 +475,14 @@ out_free_mem:
}
/*
- * clear WDM_READ flag and possibly submit the read urb if resp_count
- * is non-zero.
+ * Submit the read urb if resp_count is non-zero.
*
* Called with desc->iuspin locked
*/
-static int clear_wdm_read_flag(struct wdm_device *desc)
+static int service_outstanding_interrupt(struct wdm_device *desc)
{
int rv = 0;
- clear_bit(WDM_READ, &desc->flags);
-
/* submit read urb only if the device is waiting for it */
if (!desc->resp_count || !--desc->resp_count)
goto out;
@@ -537,8 +573,9 @@ retry:
}
if (!desc->reslength) { /* zero length read */
- dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
- rv = clear_wdm_read_flag(desc);
+ dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n");
+ clear_bit(WDM_READ, &desc->flags);
+ rv = service_outstanding_interrupt(desc);
spin_unlock_irq(&desc->iuspin);
if (rv < 0)
goto err;
@@ -563,8 +600,10 @@ retry:
desc->length -= cntr;
/* in case we had outstanding data */
- if (!desc->length)
- clear_wdm_read_flag(desc);
+ if (!desc->length) {
+ clear_bit(WDM_READ, &desc->flags);
+ service_outstanding_interrupt(desc);
+ }
spin_unlock_irq(&desc->iuspin);
rv = cntr;
@@ -647,6 +686,17 @@ static int wdm_open(struct inode *inode, struct file *file)
dev_err(&desc->intf->dev,
"Error submitting int urb - %d\n", rv);
rv = usb_translate_errors(rv);
+ } else if (test_bit(WDM_DRAIN_ON_OPEN, &desc->flags)) {
+ /*
+ * Some devices keep pending messages queued
+ * without resending notifications. We must
+ * flush the message queue before we can
+ * assume a one-to-one relationship between
+ * notifications and messages in the queue
+ */
+ dev_dbg(&desc->intf->dev, "draining queued data\n");
+ set_bit(WDM_RESPONDING, &desc->flags);
+ rv = usb_submit_urb(desc->response, GFP_KERNEL);
}
} else {
rv = 0;
@@ -673,7 +723,7 @@ static int wdm_release(struct inode *inode, struct file *file)
if (!desc->count) {
if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
- dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
+ dev_dbg(&desc->intf->dev, "wdm_release: cleanup\n");
kill_urbs(desc);
spin_lock_irq(&desc->iuspin);
desc->resp_count = 0;
@@ -753,7 +803,8 @@ static void wdm_rxwork(struct work_struct *work)
/* --- hotplug --- */
static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor *ep,
- u16 bufsize, int (*manage_power)(struct usb_interface *, int))
+ u16 bufsize, int (*manage_power)(struct usb_interface *, int),
+ bool drain_on_open)
{
int rv = -ENOMEM;
struct wdm_device *desc;
@@ -840,6 +891,68 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor
desc->manage_power = manage_power;
+ /*
+ * "drain_on_open" enables a hack to work around a firmware
+ * issue observed on network functions, in particular MBIM
+ * functions.
+ *
+ * Quoting section 7 of the CDC-WMC r1.1 specification:
+ *
+ * "The firmware shall interpret GetEncapsulatedResponse as a
+ * request to read response bytes. The firmware shall send
+ * the next wLength bytes from the response. The firmware
+ * shall allow the host to retrieve data using any number of
+ * GetEncapsulatedResponse requests. The firmware shall
+ * return a zero- length reply if there are no data bytes
+ * available.
+ *
+ * The firmware shall send ResponseAvailable notifications
+ * periodically, using any appropriate algorithm, to inform
+ * the host that there is data available in the reply
+ * buffer. The firmware is allowed to send ResponseAvailable
+ * notifications even if there is no data available, but
+ * this will obviously reduce overall performance."
+ *
+ * These requirements, although they make equally sense, are
+ * often not implemented by network functions. Some firmwares
+ * will queue data indefinitely, without ever resending a
+ * notification. The result is that the driver and firmware
+ * loses "syncronization" if the driver ever fails to respond
+ * to a single notification, something which easily can happen
+ * on release(). When this happens, the driver will appear to
+ * never receive notifications for the most current data. Each
+ * notification will only cause a single read, which returns
+ * the oldest data in the firmware's queue.
+ *
+ * The "drain_on_open" hack resolves the situation by draining
+ * data from the firmware until none is returned, without a
+ * prior notification.
+ *
+ * This will inevitably race with the firmware, risking that
+ * we read data from the device before handling the associated
+ * notification. To make things worse, some of the devices
+ * needing the hack do not implement the "return zero if no
+ * data is available" requirement either. Instead they return
+ * an error on the subsequent read in this case. This means
+ * that "winning" the race can cause an unexpected EIO to
+ * userspace.
+ *
+ * "winning" the race is more likely on resume() than on
+ * open(), and the unexpected error is more harmful in the
+ * middle of an open session. The hack is therefore only
+ * applied on open(), and not on resume() where it logically
+ * would be equally necessary. So we define open() as the only
+ * driver <-> device "syncronization point". Should we happen
+ * to lose a notification after open(), then syncronization
+ * will be lost until release()
+ *
+ * The hack should not be enabled for CDC WDM devices
+ * conforming to the CDC-WMC r1.1 specification. This is
+ * ensured by setting drain_on_open to false in wdm_probe().
+ */
+ if (drain_on_open)
+ set_bit(WDM_DRAIN_ON_OPEN, &desc->flags);
+
spin_lock(&wdm_device_list_lock);
list_add(&desc->device_list, &wdm_device_list);
spin_unlock(&wdm_device_list_lock);
@@ -893,7 +1006,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
goto err;
ep = &iface->endpoint[0].desc;
- rv = wdm_create(intf, ep, maxcom, &wdm_manage_power);
+ rv = wdm_create(intf, ep, maxcom, &wdm_manage_power, false);
err:
return rv;
@@ -925,7 +1038,7 @@ struct usb_driver *usb_cdc_wdm_register(struct usb_interface *intf,
{
int rv = -EINVAL;
- rv = wdm_create(intf, ep, bufsize, manage_power);
+ rv = wdm_create(intf, ep, bufsize, manage_power, true);
if (rv < 0)
goto err;
@@ -967,7 +1080,7 @@ static void wdm_disconnect(struct usb_interface *intf)
if (!desc->count)
cleanup(desc);
else
- dev_dbg(&intf->dev, "%s: %d open files - postponing cleanup\n", __func__, desc->count);
+ dev_dbg(&intf->dev, "%d open files - postponing cleanup\n", desc->count);
mutex_unlock(&wdm_mutex);
}
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 917a55c..a6c1fae 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -141,6 +141,7 @@ static void usbtmc_delete(struct kref *kref)
struct usbtmc_device_data *data = to_usbtmc_data(kref);
usb_put_dev(data->usb_dev);
+ kfree(data);
}
static int usbtmc_open(struct inode *inode, struct file *filp)
@@ -1379,7 +1380,7 @@ static int usbtmc_probe(struct usb_interface *intf,
dev_dbg(&intf->dev, "%s called\n", __func__);
- data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -1467,10 +1468,8 @@ static int usbtmc_probe(struct usb_interface *intf,
if (data->iin_ep_present) {
/* allocate int urb */
data->iin_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!data->iin_urb) {
- dev_err(&intf->dev, "Failed to allocate int urb\n");
+ if (!data->iin_urb)
goto error_register;
- }
/* will reference data in int urb */
kref_get(&data->kref);
@@ -1478,10 +1477,8 @@ static int usbtmc_probe(struct usb_interface *intf,
/* allocate buffer for interrupt in */
data->iin_buffer = kmalloc(data->iin_wMaxPacketSize,
GFP_KERNEL);
- if (!data->iin_buffer) {
- dev_err(&intf->dev, "Failed to allocate int buf\n");
+ if (!data->iin_buffer)
goto error_register;
- }
/* fill interrupt urb */
usb_fill_int_urb(data->iin_urb, data->usb_dev,
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 01c0c04..8b31770 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -21,13 +21,13 @@
int ulpi_read(struct ulpi *ulpi, u8 addr)
{
- return ulpi->ops->read(ulpi->ops, addr);
+ return ulpi->ops->read(ulpi->dev.parent, addr);
}
EXPORT_SYMBOL_GPL(ulpi_read);
int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val)
{
- return ulpi->ops->write(ulpi->ops, addr, val);
+ return ulpi->ops->write(ulpi->dev.parent, addr, val);
}
EXPORT_SYMBOL_GPL(ulpi_write);
@@ -127,16 +127,17 @@ static struct device_type ulpi_dev_type = {
*
* Registers a driver with the ULPI bus.
*/
-int ulpi_register_driver(struct ulpi_driver *drv)
+int __ulpi_register_driver(struct ulpi_driver *drv, struct module *module)
{
if (!drv->probe)
return -EINVAL;
+ drv->driver.owner = module;
drv->driver.bus = &ulpi_bus;
return driver_register(&drv->driver);
}
-EXPORT_SYMBOL_GPL(ulpi_register_driver);
+EXPORT_SYMBOL_GPL(__ulpi_register_driver);
/**
* ulpi_unregister_driver - unregister a driver with the ULPI bus
@@ -156,6 +157,8 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
{
int ret;
+ ulpi->dev.parent = dev; /* needed early for ops */
+
/* Test the interface */
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
if (ret < 0)
@@ -174,7 +177,6 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
- ulpi->dev.parent = dev;
ulpi->dev.bus = &ulpi_bus;
ulpi->dev.type = &ulpi_dev_type;
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
@@ -201,7 +203,8 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
* Allocates and registers a ULPI device and an interface for it. Called from
* the USB controller that provides the ULPI interface.
*/
-struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops)
+struct ulpi *ulpi_register_interface(struct device *dev,
+ const struct ulpi_ops *ops)
{
struct ulpi *ulpi;
int ret;
@@ -211,7 +214,6 @@ struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops)
return ERR_PTR(-ENOMEM);
ulpi->ops = ops;
- ops->dev = dev;
ret = ulpi_register(dev, ulpi);
if (ret) {
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index dd28010..0e5a889 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -83,23 +83,10 @@ config USB_OTG_FSM
Implements OTG Finite State Machine as specified in On-The-Go
and Embedded Host Supplement to the USB Revision 2.0 Specification.
-config USB_ULPI_BUS
- tristate "USB ULPI PHY interface support"
- depends on USB_SUPPORT
+config USB_LEDS_TRIGGER_USBPORT
+ tristate "USB port LED trigger"
+ depends on USB && LEDS_TRIGGERS
help
- UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
- USB 2.0 PHY interface. The ULPI specification defines a standard set
- of registers that can be used to detect the vendor and product which
- allows ULPI to be handled as a bus. This module is the driver for that
- bus.
-
- The ULPI interfaces (the buses) are registered by the drivers for USB
- controllers which support ULPI register access and have ULPI PHY
- attached to them. The ULPI PHY drivers themselves are normal PHY
- drivers.
-
- ULPI PHYs provide often functions such as ADP sensing/probing (OTG
- protocol) and USB charger detection.
-
- To compile this driver as a module, choose M here: the module will
- be called ulpi.
+ This driver allows LEDs to be controlled by USB events. Enabling this
+ trigger allows specifying list of USB ports that should turn on LED
+ when some USB device gets connected.
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 9780877..b99b871 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -5,9 +5,12 @@
usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o
usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
usbcore-y += devio.o notify.o generic.o quirks.o devices.o
-usbcore-y += port.o of.o
+usbcore-y += port.o
+usbcore-$(CONFIG_OF) += of.o
usbcore-$(CONFIG_PCI) += hcd-pci.o
usbcore-$(CONFIG_ACPI) += usb-acpi.o
obj-$(CONFIG_USB) += usbcore.o
+
+obj-$(CONFIG_USB_LEDS_TRIGGER_USBPORT) += ledtrig-usbport.o
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index d2e3f65..479e223 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -46,6 +46,7 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/phy.h>
+#include <linux/usb/otg.h>
#include "usb.h"
@@ -2517,10 +2518,8 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver,
struct usb_hcd *hcd;
hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL);
- if (!hcd) {
- dev_dbg (dev, "hcd alloc failed\n");
+ if (!hcd)
return NULL;
- }
if (primary_hcd == NULL) {
hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
GFP_KERNEL);
@@ -3033,7 +3032,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
/*-------------------------------------------------------------------------*/
-#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+#if IS_ENABLED(CONFIG_USB_MON)
const struct usb_mon_operations *mon_ops;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 1d5fc32..cbb1467 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1823,10 +1823,8 @@ descriptor_error:
dev_info(&intf->dev, "USB hub found\n");
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
- if (!hub) {
- dev_dbg(&intf->dev, "couldn't kmalloc hub struct\n");
+ if (!hub)
return -ENOMEM;
- }
kref_init(&hub->kref);
hub->intfdev = &intf->dev;
@@ -3106,7 +3104,7 @@ static int usb_disable_remote_wakeup(struct usb_device *udev)
USB_CTRL_SET_TIMEOUT);
else
return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE,
+ USB_REQ_SET_FEATURE, USB_RECIP_INTERFACE,
USB_INTRF_FUNC_SUSPEND, 0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
@@ -5337,11 +5335,10 @@ static int descriptors_changed(struct usb_device *udev,
}
buf = kmalloc(len, GFP_NOIO);
- if (buf == NULL) {
- dev_err(&udev->dev, "no mem to re-read configs after reset\n");
+ if (!buf)
/* assume the worst */
return 1;
- }
+
for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,
diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c
new file mode 100644
index 0000000..3ed5162
--- /dev/null
+++ b/drivers/usb/core/ledtrig-usbport.c
@@ -0,0 +1,314 @@
+/*
+ * USB port LED trigger
+ *
+ * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+struct usbport_trig_data {
+ struct led_classdev *led_cdev;
+ struct list_head ports;
+ struct notifier_block nb;
+ int count; /* Amount of connected matching devices */
+};
+
+struct usbport_trig_port {
+ struct usbport_trig_data *data;
+ struct usb_device *hub;
+ int portnum;
+ char *port_name;
+ bool observed;
+ struct device_attribute attr;
+ struct list_head list;
+};
+
+/***************************************
+ * Helpers
+ ***************************************/
+
+/**
+ * usbport_trig_usb_dev_observed - Check if dev is connected to observed port
+ */
+static bool usbport_trig_usb_dev_observed(struct usbport_trig_data *usbport_data,
+ struct usb_device *usb_dev)
+{
+ struct usbport_trig_port *port;
+
+ if (!usb_dev->parent)
+ return false;
+
+ list_for_each_entry(port, &usbport_data->ports, list) {
+ if (usb_dev->parent == port->hub &&
+ usb_dev->portnum == port->portnum)
+ return port->observed;
+ }
+
+ return false;
+}
+
+static int usbport_trig_usb_dev_check(struct usb_device *usb_dev, void *data)
+{
+ struct usbport_trig_data *usbport_data = data;
+
+ if (usbport_trig_usb_dev_observed(usbport_data, usb_dev))
+ usbport_data->count++;
+
+ return 0;
+}
+
+/**
+ * usbport_trig_update_count - Recalculate amount of connected matching devices
+ */
+static void usbport_trig_update_count(struct usbport_trig_data *usbport_data)
+{
+ struct led_classdev *led_cdev = usbport_data->led_cdev;
+
+ usbport_data->count = 0;
+ usb_for_each_dev(usbport_data, usbport_trig_usb_dev_check);
+ led_cdev->brightness_set(led_cdev,
+ usbport_data->count ? LED_FULL : LED_OFF);
+}
+
+/***************************************
+ * Device attr
+ ***************************************/
+
+static ssize_t usbport_trig_port_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usbport_trig_port *port = container_of(attr,
+ struct usbport_trig_port,
+ attr);
+
+ return sprintf(buf, "%d\n", port->observed) + 1;
+}
+
+static ssize_t usbport_trig_port_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct usbport_trig_port *port = container_of(attr,
+ struct usbport_trig_port,
+ attr);
+
+ if (!strcmp(buf, "0") || !strcmp(buf, "0\n"))
+ port->observed = 0;
+ else if (!strcmp(buf, "1") || !strcmp(buf, "1\n"))
+ port->observed = 1;
+ else
+ return -EINVAL;
+
+ usbport_trig_update_count(port->data);
+
+ return size;
+}
+
+static struct attribute *ports_attrs[] = {
+ NULL,
+};
+static const struct attribute_group ports_group = {
+ .name = "ports",
+ .attrs = ports_attrs,
+};
+
+/***************************************
+ * Adding & removing ports
+ ***************************************/
+
+static int usbport_trig_add_port(struct usbport_trig_data *usbport_data,
+ struct usb_device *usb_dev,
+ const char *hub_name, int portnum)
+{
+ struct led_classdev *led_cdev = usbport_data->led_cdev;
+ struct usbport_trig_port *port;
+ size_t len;
+ int err;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ port->data = usbport_data;
+ port->hub = usb_dev;
+ port->portnum = portnum;
+
+ len = strlen(hub_name) + 8;
+ port->port_name = kzalloc(len, GFP_KERNEL);
+ if (!port->port_name) {
+ err = -ENOMEM;
+ goto err_free_port;
+ }
+ snprintf(port->port_name, len, "%s-port%d", hub_name, portnum);
+
+ port->attr.attr.name = port->port_name;
+ port->attr.attr.mode = S_IRUSR | S_IWUSR;
+ port->attr.show = usbport_trig_port_show;
+ port->attr.store = usbport_trig_port_store;
+
+ err = sysfs_add_file_to_group(&led_cdev->dev->kobj, &port->attr.attr,
+ ports_group.name);
+ if (err)
+ goto err_free_port_name;
+
+ list_add_tail(&port->list, &usbport_data->ports);
+
+ return 0;
+
+err_free_port_name:
+ kfree(port->port_name);
+err_free_port:
+ kfree(port);
+err_out:
+ return err;
+}
+
+static int usbport_trig_add_usb_dev_ports(struct usb_device *usb_dev,
+ void *data)
+{
+ struct usbport_trig_data *usbport_data = data;
+ int i;
+
+ for (i = 1; i <= usb_dev->maxchild; i++)
+ usbport_trig_add_port(usbport_data, usb_dev,
+ dev_name(&usb_dev->dev), i);
+
+ return 0;
+}
+
+static void usbport_trig_remove_port(struct usbport_trig_data *usbport_data,
+ struct usbport_trig_port *port)
+{
+ struct led_classdev *led_cdev = usbport_data->led_cdev;
+
+ list_del(&port->list);
+ sysfs_remove_file_from_group(&led_cdev->dev->kobj, &port->attr.attr,
+ ports_group.name);
+ kfree(port->port_name);
+ kfree(port);
+}
+
+static void usbport_trig_remove_usb_dev_ports(struct usbport_trig_data *usbport_data,
+ struct usb_device *usb_dev)
+{
+ struct usbport_trig_port *port, *tmp;
+
+ list_for_each_entry_safe(port, tmp, &usbport_data->ports, list) {
+ if (port->hub == usb_dev)
+ usbport_trig_remove_port(usbport_data, port);
+ }
+}
+
+/***************************************
+ * Init, exit, etc.
+ ***************************************/
+
+static int usbport_trig_notify(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct usbport_trig_data *usbport_data =
+ container_of(nb, struct usbport_trig_data, nb);
+ struct led_classdev *led_cdev = usbport_data->led_cdev;
+ struct usb_device *usb_dev = data;
+ bool observed;
+
+ observed = usbport_trig_usb_dev_observed(usbport_data, usb_dev);
+
+ switch (action) {
+ case USB_DEVICE_ADD:
+ usbport_trig_add_usb_dev_ports(usb_dev, usbport_data);
+ if (observed && usbport_data->count++ == 0)
+ led_cdev->brightness_set(led_cdev, LED_FULL);
+ return NOTIFY_OK;
+ case USB_DEVICE_REMOVE:
+ usbport_trig_remove_usb_dev_ports(usbport_data, usb_dev);
+ if (observed && --usbport_data->count == 0)
+ led_cdev->brightness_set(led_cdev, LED_OFF);
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static void usbport_trig_activate(struct led_classdev *led_cdev)
+{
+ struct usbport_trig_data *usbport_data;
+ int err;
+
+ usbport_data = kzalloc(sizeof(*usbport_data), GFP_KERNEL);
+ if (!usbport_data)
+ return;
+ usbport_data->led_cdev = led_cdev;
+
+ /* List of ports */
+ INIT_LIST_HEAD(&usbport_data->ports);
+ err = sysfs_create_group(&led_cdev->dev->kobj, &ports_group);
+ if (err)
+ goto err_free;
+ usb_for_each_dev(usbport_data, usbport_trig_add_usb_dev_ports);
+
+ /* Notifications */
+ usbport_data->nb.notifier_call = usbport_trig_notify,
+ led_cdev->trigger_data = usbport_data;
+ usb_register_notify(&usbport_data->nb);
+
+ led_cdev->activated = true;
+ return;
+
+err_free:
+ kfree(usbport_data);
+}
+
+static void usbport_trig_deactivate(struct led_classdev *led_cdev)
+{
+ struct usbport_trig_data *usbport_data = led_cdev->trigger_data;
+ struct usbport_trig_port *port, *tmp;
+
+ if (!led_cdev->activated)
+ return;
+
+ list_for_each_entry_safe(port, tmp, &usbport_data->ports, list) {
+ usbport_trig_remove_port(usbport_data, port);
+ }
+
+ usb_unregister_notify(&usbport_data->nb);
+
+ sysfs_remove_group(&led_cdev->dev->kobj, &ports_group);
+
+ kfree(usbport_data);
+
+ led_cdev->activated = false;
+}
+
+static struct led_trigger usbport_led_trigger = {
+ .name = "usbport",
+ .activate = usbport_trig_activate,
+ .deactivate = usbport_trig_deactivate,
+};
+
+static int __init usbport_trig_init(void)
+{
+ return led_trigger_register(&usbport_led_trigger);
+}
+
+static void __exit usbport_trig_exit(void)
+{
+ led_trigger_unregister(&usbport_led_trigger);
+}
+
+module_init(usbport_trig_init);
+module_exit(usbport_trig_exit);
+
+MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>");
+MODULE_DESCRIPTION("USB port trigger");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 0406a59..3a47077 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1760,17 +1760,14 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
nintf = cp->desc.bNumInterfaces;
new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),
GFP_NOIO);
- if (!new_interfaces) {
- dev_err(&dev->dev, "Out of memory\n");
+ if (!new_interfaces)
return -ENOMEM;
- }
for (; n < nintf; ++n) {
new_interfaces[n] = kzalloc(
sizeof(struct usb_interface),
GFP_NOIO);
if (!new_interfaces[n]) {
- dev_err(&dev->dev, "Out of memory\n");
ret = -ENOMEM;
free_interfaces:
while (--n >= 0)
@@ -1862,7 +1859,12 @@ free_interfaces:
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
intf->dev.groups = usb_interface_groups;
+ /*
+ * Please refer to usb_alloc_dev() to see why we set
+ * dma_mask and dma_pfn_offset.
+ */
intf->dev.dma_mask = dev->dev.dma_mask;
+ intf->dev.dma_pfn_offset = dev->dev.dma_pfn_offset;
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
intf->minor = -1;
device_initialize(&intf->dev);
diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c
index 2289700..3de4f88 100644
--- a/drivers/usb/core/of.c
+++ b/drivers/usb/core/of.c
@@ -18,6 +18,7 @@
*/
#include <linux/of.h>
+#include <linux/usb/of.h>
/**
* usb_of_get_child_node - Find the device node match port number
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
index a95b0c9..085049d 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_whitelist.h
@@ -38,7 +38,7 @@ static struct usb_device_id whitelist_table[] = {
{ USB_DEVICE(0x0525, 0xa4a2), },
#endif
-#if defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE)
+#if IS_ENABLED(CONFIG_USB_TEST)
/* gadget zero, for testing */
{ USB_DEVICE(0x0525, 0xa4a0), },
#endif
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index c601e25..a903969 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -68,10 +68,8 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
urb = kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);
- if (!urb) {
- printk(KERN_ERR "alloc_urb: kmalloc failed\n");
+ if (!urb)
return NULL;
- }
usb_init_urb(urb);
return urb;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 5e80697..5921514 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -440,7 +440,18 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.groups = usb_device_groups;
+ /*
+ * Fake a dma_mask/offset for the USB device:
+ * We cannot really use the dma-mapping API (dma_alloc_* and
+ * dma_map_*) for USB devices but instead need to use
+ * usb_alloc_coherent and pass data in 'urb's, but some subsystems
+ * manually look into the mask/offset pair to determine whether
+ * they need bounce buffers.
+ * Note: calling dma_set_mask() on a USB device would set the
+ * mask for the entire HCD, so don't do that.
+ */
dev->dev.dma_mask = bus->controller->dma_mask;
+ dev->dev.dma_pfn_offset = bus->controller->dma_pfn_offset;
set_dev_node(&dev->dev, dev_to_node(bus->controller));
dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 4135a5f..fa9b26b 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -238,6 +238,77 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
return ret;
}
+/**
+ * dwc2_wait_for_mode() - Waits for the controller mode.
+ * @hsotg: Programming view of the DWC_otg controller.
+ * @host_mode: If true, waits for host mode, otherwise device mode.
+ */
+static void dwc2_wait_for_mode(struct dwc2_hsotg *hsotg,
+ bool host_mode)
+{
+ ktime_t start;
+ ktime_t end;
+ unsigned int timeout = 110;
+
+ dev_vdbg(hsotg->dev, "Waiting for %s mode\n",
+ host_mode ? "host" : "device");
+
+ start = ktime_get();
+
+ while (1) {
+ s64 ms;
+
+ if (dwc2_is_host_mode(hsotg) == host_mode) {
+ dev_vdbg(hsotg->dev, "%s mode set\n",
+ host_mode ? "Host" : "Device");
+ break;
+ }
+
+ end = ktime_get();
+ ms = ktime_to_ms(ktime_sub(end, start));
+
+ if (ms >= (s64)timeout) {
+ dev_warn(hsotg->dev, "%s: Couldn't set %s mode\n",
+ __func__, host_mode ? "host" : "device");
+ break;
+ }
+
+ usleep_range(1000, 2000);
+ }
+}
+
+/**
+ * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce
+ * filter is enabled.
+ */
+static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
+{
+ u32 gsnpsid;
+ u32 ghwcfg4;
+
+ if (!dwc2_hw_is_otg(hsotg))
+ return false;
+
+ /* Check if core configuration includes the IDDIG filter. */
+ ghwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4);
+ if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN))
+ return false;
+
+ /*
+ * Check if the IDDIG debounce filter is bypassed. Available
+ * in core version >= 3.10a.
+ */
+ gsnpsid = dwc2_readl(hsotg->regs + GSNPSID);
+ if (gsnpsid >= DWC2_CORE_REV_3_10a) {
+ u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+
+ if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS)
+ return false;
+ }
+
+ return true;
+}
+
/*
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
@@ -246,9 +317,30 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
{
u32 greset;
int count = 0;
+ bool wait_for_host_mode = false;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
+ /*
+ * If the current mode is host, either due to the force mode
+ * bit being set (which persists after core reset) or the
+ * connector id pin, a core soft reset will temporarily reset
+ * the mode to device. A delay from the IDDIG debounce filter
+ * will occur before going back to host mode.
+ *
+ * Determine whether we will go back into host mode after a
+ * reset and account for this delay after the reset.
+ */
+ if (dwc2_iddig_filter_enabled(hsotg)) {
+ u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
+ u32 gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
+
+ if (!(gotgctl & GOTGCTL_CONID_B) ||
+ (gusbcfg & GUSBCFG_FORCEHOSTMODE)) {
+ wait_for_host_mode = true;
+ }
+ }
+
/* Core Soft Reset */
greset = dwc2_readl(hsotg->regs + GRSTCTL);
greset |= GRSTCTL_CSFTRST;
@@ -277,6 +369,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
}
} while (!(greset & GRSTCTL_AHBIDLE));
+ if (wait_for_host_mode)
+ dwc2_wait_for_mode(hsotg, true);
+
return 0;
}
@@ -300,9 +395,9 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg)
* Checks are done in this function to determine whether doing a force
* would be valid or not.
*
- * If a force is done, it requires a 25ms delay to take effect.
- *
- * Returns true if the mode was forced.
+ * If a force is done, it requires a IDDIG debounce filter delay if
+ * the filter is configured and enabled. We poll the current mode of
+ * the controller to account for this delay.
*/
static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{
@@ -337,12 +432,18 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
gusbcfg |= set;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
- msleep(25);
+ dwc2_wait_for_mode(hsotg, host);
return true;
}
-/*
- * Clears the force mode bits.
+/**
+ * dwc2_clear_force_mode() - Clears the force mode bits.
+ *
+ * After clearing the bits, wait up to 100 ms to account for any
+ * potential IDDIG filter delay. We can't know if we expect this delay
+ * or not because the value of the connector ID status is affected by
+ * the force mode. We only need to call this once during probe if
+ * dr_mode == OTG.
*/
static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
@@ -353,11 +454,8 @@ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
- /*
- * NOTE: This long sleep is _very_ important, otherwise the core will
- * not stay in host mode after a connector ID change!
- */
- msleep(25);
+ if (dwc2_iddig_filter_enabled(hsotg))
+ usleep_range(100000, 110000);
}
/*
@@ -380,12 +478,6 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
__func__, hsotg->dr_mode);
break;
}
-
- /*
- * NOTE: This is required for some rockchip soc based
- * platforms.
- */
- msleep(50);
}
/*
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index d645512..aad4107 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -259,13 +259,6 @@ enum dwc2_lx_state {
DWC2_L3, /* Off state */
};
-/*
- * Gadget periodic tx fifo sizes as used by legacy driver
- * EP0 is not included
- */
-#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
- 768, 0, 0, 0, 0, 0, 0, 0}
-
/* Gadget ep0 states */
enum dwc2_ep0_state {
DWC2_EP0_SETUP,
@@ -890,6 +883,7 @@ struct dwc2_hsotg {
#define DWC2_CORE_REV_2_92a 0x4f54292a
#define DWC2_CORE_REV_2_94a 0x4f54294a
#define DWC2_CORE_REV_3_00a 0x4f54300a
+#define DWC2_CORE_REV_3_10a 0x4f54310a
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index af46adf..4cd6403 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -186,9 +186,10 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
*/
static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
{
- unsigned int ep;
+ unsigned int fifo;
unsigned int addr;
int timeout;
+ u32 dptxfsizn;
u32 val;
/* Reset fifo map if not correctly cleared during previous session */
@@ -216,16 +217,16 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
* them to endpoints dynamically according to maxpacket size value of
* given endpoint.
*/
- for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) {
- if (!hsotg->g_tx_fifo_sz[ep])
- continue;
- val = addr;
- val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT;
- WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem,
- "insufficient fifo memory");
- addr += hsotg->g_tx_fifo_sz[ep];
+ for (fifo = 1; fifo < MAX_EPS_CHANNELS; fifo++) {
+ dptxfsizn = dwc2_readl(hsotg->regs + DPTXFSIZN(fifo));
+
+ val = (dptxfsizn & FIFOSIZE_DEPTH_MASK) | addr;
+ addr += dptxfsizn >> FIFOSIZE_DEPTH_SHIFT;
+
+ if (addr > hsotg->fifo_mem)
+ break;
- dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep));
+ dwc2_writel(val, hsotg->regs + DPTXFSIZN(fifo));
}
/*
@@ -388,7 +389,8 @@ static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
return -ENOSPC;
}
} else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
- can_write = dwc2_readl(hsotg->regs + DTXFSTS(hs_ep->index));
+ can_write = dwc2_readl(hsotg->regs +
+ DTXFSTS(hs_ep->fifo_index));
can_write &= 0xffff;
can_write *= 4;
@@ -2432,7 +2434,7 @@ static void kill_all_requests(struct dwc2_hsotg *hsotg,
if (!hsotg->dedicated_fifos)
return;
- size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4;
+ size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->fifo_index)) & 0xffff) * 4;
if (size < ep->fifo_size)
dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index);
}
@@ -3041,22 +3043,11 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
break;
}
- /* If fifo is already allocated for this ep */
- if (hs_ep->fifo_index) {
- size = hs_ep->ep.maxpacket * hs_ep->mc;
- /* If bigger fifo is required deallocate current one */
- if (size > hs_ep->fifo_size) {
- hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
- hs_ep->fifo_index = 0;
- hs_ep->fifo_size = 0;
- }
- }
-
/*
* if the hardware has dedicated fifos, we must give each IN EP
* a unique tx-fifo even if it is non-periodic.
*/
- if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) {
+ if (dir_in && hsotg->dedicated_fifos) {
u32 fifo_index = 0;
u32 fifo_size = UINT_MAX;
size = hs_ep->ep.maxpacket*hs_ep->mc;
@@ -3129,10 +3120,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
spin_lock_irqsave(&hsotg->lock, flags);
- hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
- hs_ep->fifo_index = 0;
- hs_ep->fifo_size = 0;
-
ctrl = dwc2_readl(hsotg->regs + epctrl_reg);
ctrl &= ~DXEPCTL_EPENA;
ctrl &= ~DXEPCTL_USBACTEP;
@@ -3147,6 +3134,10 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep)
/* terminate all requests with shutdown */
kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
+ hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
+ hs_ep->fifo_index = 0;
+ hs_ep->fifo_size = 0;
+
spin_unlock_irqrestore(&hsotg->lock, flags);
return 0;
}
@@ -3475,8 +3466,11 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
spin_lock_irqsave(&hsotg->lock, flags);
- dwc2_hsotg_init(hsotg);
- dwc2_hsotg_core_init_disconnected(hsotg, false);
+ if (dwc2_hw_is_device(hsotg)) {
+ dwc2_hsotg_init(hsotg);
+ dwc2_hsotg_core_init_disconnected(hsotg, false);
+ }
+
hsotg->enabled = 0;
spin_unlock_irqrestore(&hsotg->lock, flags);
@@ -3813,36 +3807,10 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg)
{
struct device_node *np = hsotg->dev->of_node;
- u32 len = 0;
- u32 i = 0;
/* Enable dma if requested in device tree */
hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
- /*
- * Register TX periodic fifo size per endpoint.
- * EP0 is excluded since it has no fifo configuration.
- */
- if (!of_find_property(np, "g-tx-fifo-size", &len))
- goto rx_fifo;
-
- len /= sizeof(u32);
-
- /* Read tx fifo sizes other than ep0 */
- if (of_property_read_u32_array(np, "g-tx-fifo-size",
- &hsotg->g_tx_fifo_sz[1], len))
- goto rx_fifo;
-
- /* Add ep0 */
- len++;
-
- /* Make remaining TX fifos unavailable */
- if (len < MAX_EPS_CHANNELS) {
- for (i = len; i < MAX_EPS_CHANNELS; i++)
- hsotg->g_tx_fifo_sz[i] = 0;
- }
-
-rx_fifo:
/* Register RX fifo size */
of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz);
@@ -3864,13 +3832,10 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
struct device *dev = hsotg->dev;
int epnum;
int ret;
- int i;
- u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE;
/* Initialize to legacy fifo configuration values */
hsotg->g_rx_fifo_sz = 2048;
hsotg->g_np_g_tx_fifo_sz = 1024;
- memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
/* Device tree specific probe */
dwc2_hsotg_of_probe(hsotg);
@@ -3888,9 +3853,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
hsotg->g_np_g_tx_fifo_sz);
dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz);
- for (i = 0; i < MAX_EPS_CHANNELS; i++)
- dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i,
- hsotg->g_tx_fifo_sz[i]);
hsotg->gadget.max_speed = USB_SPEED_HIGH;
hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
@@ -3908,17 +3870,13 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
- if (!hsotg->ctrl_buff) {
- dev_err(dev, "failed to allocate ctrl request buff\n");
+ if (!hsotg->ctrl_buff)
return -ENOMEM;
- }
hsotg->ep0_buff = devm_kzalloc(hsotg->dev,
DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
- if (!hsotg->ep0_buff) {
- dev_err(dev, "failed to allocate ctrl reply buff\n");
+ if (!hsotg->ep0_buff)
return -ENOMEM;
- }
ret = devm_request_irq(hsotg->dev, irq, dwc2_hsotg_irq, IRQF_SHARED,
dev_name(hsotg->dev), hsotg);
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 2df3d04..df5a065 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -5040,7 +5040,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
/* Create new workqueue and init work */
retval = -ENOMEM;
- hsotg->wq_otg = create_singlethread_workqueue("dwc2");
+ hsotg->wq_otg = alloc_ordered_workqueue("dwc2", 0);
if (!hsotg->wq_otg) {
dev_err(hsotg->dev, "Failed to create workqueue\n");
goto error2;
diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h
index efc3bcd..9105844 100644
--- a/drivers/usb/dwc2/hw.h
+++ b/drivers/usb/dwc2/hw.h
@@ -48,6 +48,7 @@
#define GOTGCTL_ASESVLD (1 << 18)
#define GOTGCTL_DBNC_SHORT (1 << 17)
#define GOTGCTL_CONID_B (1 << 16)
+#define GOTGCTL_DBNCE_FLTR_BYPASS (1 << 15)
#define GOTGCTL_DEVHNPEN (1 << 11)
#define GOTGCTL_HSTSETHNPEN (1 << 10)
#define GOTGCTL_HNPREQ (1 << 9)
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index a64ce1c..b97cde7 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -1,7 +1,7 @@
config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
depends on (USB || USB_GADGET) && HAS_DMA
- select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
+ select USB_XHCI_PLATFORM if USB_XHCI_HCD
help
Say Y or M here if your system has a Dual Role SuperSpeed
USB controller based on the DesignWare USB3 IP Core.
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 35d0924..7287a76 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -49,6 +49,57 @@
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
+/**
+ * dwc3_get_dr_mode - Validates and sets dr_mode
+ * @dwc: pointer to our context structure
+ */
+static int dwc3_get_dr_mode(struct dwc3 *dwc)
+{
+ enum usb_dr_mode mode;
+ struct device *dev = dwc->dev;
+ unsigned int hw_mode;
+
+ if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
+ dwc->dr_mode = USB_DR_MODE_OTG;
+
+ mode = dwc->dr_mode;
+ hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0);
+
+ switch (hw_mode) {
+ case DWC3_GHWPARAMS0_MODE_GADGET:
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) {
+ dev_err(dev,
+ "Controller does not support host mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_PERIPHERAL;
+ break;
+ case DWC3_GHWPARAMS0_MODE_HOST:
+ if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
+ dev_err(dev,
+ "Controller does not support device mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_HOST;
+ break;
+ default:
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+ mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ mode = USB_DR_MODE_PERIPHERAL;
+ }
+
+ if (mode != dwc->dr_mode) {
+ dev_warn(dev,
+ "Configuration mismatch. dr_mode forced to %s\n",
+ mode == USB_DR_MODE_HOST ? "host" : "gadget");
+
+ dwc->dr_mode = mode;
+ }
+
+ return 0;
+}
+
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -448,6 +499,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_u3_susphy_quirk)
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ if (dwc->dis_del_phy_power_chg_quirk)
+ reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
+
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
@@ -485,6 +539,23 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
break;
}
+ switch (dwc->hsphy_mode) {
+ case USBPHY_INTERFACE_MODE_UTMI:
+ reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+ DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+ reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) |
+ DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT);
+ break;
+ case USBPHY_INTERFACE_MODE_UTMIW:
+ reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+ DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+ reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) |
+ DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT);
+ break;
+ default:
+ break;
+ }
+
/*
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
* '0' during coreConsultant configuration. So default value will
@@ -500,6 +571,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ if (dwc->dis_u2_freeclk_exists_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
+
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
return 0;
@@ -666,6 +740,32 @@ static int dwc3_core_init(struct dwc3 *dwc)
goto err4;
}
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ break;
+ case USB_DR_MODE_HOST:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+ break;
+ case USB_DR_MODE_OTG:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ break;
+ default:
+ dev_warn(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode);
+ break;
+ }
+
+ /*
+ * ENDXFER polling is available on version 3.10a and later of
+ * the DWC_usb3 controller. It is NOT available in the
+ * DWC_usb31 controller.
+ */
+ if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
+ reg |= DWC3_GUCTL2_RST_ACTBITLATER;
+ dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
+ }
+
return 0;
err4:
@@ -763,7 +863,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -772,7 +871,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_HOST:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -781,7 +879,6 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_OTG:
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
ret = dwc3_host_init(dwc);
if (ret) {
if (ret != -EPROBE_DEFER)
@@ -888,6 +985,7 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
+ dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
@@ -924,6 +1022,10 @@ static int dwc3_probe(struct platform_device *pdev)
"snps,dis_enblslpm_quirk");
dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
"snps,dis_rxdet_inp3_quirk");
+ dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
+ "snps,dis-u2-freeclk-exists-quirk");
+ dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev,
+ "snps,dis-del-phy-power-chg-quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
@@ -972,17 +1074,9 @@ static int dwc3_probe(struct platform_device *pdev)
goto err2;
}
- if (IS_ENABLED(CONFIG_USB_DWC3_HOST) &&
- (dwc->dr_mode == USB_DR_MODE_OTG ||
- dwc->dr_mode == USB_DR_MODE_UNKNOWN))
- dwc->dr_mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
- (dwc->dr_mode == USB_DR_MODE_OTG ||
- dwc->dr_mode == USB_DR_MODE_UNKNOWN))
- dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
-
- if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
- dwc->dr_mode = USB_DR_MODE_OTG;
+ ret = dwc3_get_dr_mode(dwc);
+ if (ret)
+ goto err3;
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 45d6de5..6b60e42 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -109,6 +109,7 @@
#define DWC3_GPRTBIMAP_HS1 0xc184
#define DWC3_GPRTBIMAP_FS0 0xc188
#define DWC3_GPRTBIMAP_FS1 0xc18c
+#define DWC3_GUCTL2 0xc19c
#define DWC3_VER_NUMBER 0xc1a0
#define DWC3_VER_TYPE 0xc1a4
@@ -199,9 +200,18 @@
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS (1 << 30)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
+#define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3)
+#define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK DWC3_GUSB2PHYCFG_USBTRDTIM(0xf)
+#define USBTRDTIM_UTMI_8_BIT 9
+#define USBTRDTIM_UTMI_16_BIT 5
+#define UTMI_PHYIF_16_BIT 1
+#define UTMI_PHYIF_8_BIT 0
/* Global USB2 PHY Vendor Control Register */
#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
@@ -235,7 +245,10 @@
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
/* Global HWPARAMS0 Register */
-#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3)
+#define DWC3_GHWPARAMS0_MODE(n) ((n) & 0x3)
+#define DWC3_GHWPARAMS0_MODE_GADGET 0
+#define DWC3_GHWPARAMS0_MODE_HOST 1
+#define DWC3_GHWPARAMS0_MODE_DRD 2
#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7)
#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3)
#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff)
@@ -279,6 +292,9 @@
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
+/* Global User Control Register 2 */
+#define DWC3_GUCTL2_RST_ACTBITLATER (1 << 14)
+
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -685,6 +701,8 @@ struct dwc3_hwparams {
* @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
+ * @num_pending_sgs: counter to pending sgs
* @first_trb_index: index to first trb used by this request
* @epnum: endpoint number to which this request refers
* @trb: pointer to struct dwc3_trb
@@ -697,7 +715,9 @@ struct dwc3_request {
struct usb_request request;
struct list_head list;
struct dwc3_ep *dep;
+ struct scatterlist *sg;
+ unsigned num_pending_sgs;
u8 first_trb_index;
u8 epnum;
struct dwc3_trb *trb;
@@ -743,6 +763,9 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
+ * @hsphy_mode: UTMI phy mode, one of following:
+ * - USBPHY_INTERFACE_MODE_UTMI
+ * - USBPHY_INTERFACE_MODE_UTMIW
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -799,6 +822,11 @@ struct dwc3_scratchpad_array {
* @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_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.
+ * @dis_del_phy_power_chg_quirk: set if we disable delay phy power
+ * change quirk.
* @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
* @tx_de_emphasis: Tx de-emphasis value
* 0 - -6dB de-emphasis
@@ -845,6 +873,7 @@ struct dwc3 {
size_t regs_size;
enum usb_dr_mode dr_mode;
+ enum usb_phy_interface hsphy_mode;
u32 fladj;
u32 irq_gadget;
@@ -880,6 +909,8 @@ struct dwc3 {
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
+#define DWC3_REVISION_300A 0x5533300a
+#define DWC3_REVISION_310A 0x5533310a
/*
* NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
@@ -942,6 +973,8 @@ struct dwc3 {
unsigned dis_u2_susphy_quirk:1;
unsigned dis_enblslpm_quirk:1;
unsigned dis_rxdet_inp3_quirk:1;
+ unsigned dis_u2_freeclk_exists_quirk:1;
+ unsigned dis_del_phy_power_chg_quirk:1;
unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index e56d59b..fe414e7 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -36,36 +36,25 @@ struct dwc3_of_simple {
int num_clocks;
};
-static int dwc3_of_simple_probe(struct platform_device *pdev)
+static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count)
{
- struct dwc3_of_simple *simple;
- struct device *dev = &pdev->dev;
+ struct device *dev = simple->dev;
struct device_node *np = dev->of_node;
-
- unsigned int count;
- int ret;
int i;
- simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
- if (!simple)
- return -ENOMEM;
+ simple->num_clocks = count;
- count = of_clk_get_parent_count(np);
if (!count)
- return -ENOENT;
-
- simple->num_clocks = count;
+ return 0;
simple->clks = devm_kcalloc(dev, simple->num_clocks,
sizeof(struct clk *), GFP_KERNEL);
if (!simple->clks)
return -ENOMEM;
- platform_set_drvdata(pdev, simple);
- simple->dev = dev;
-
for (i = 0; i < simple->num_clocks; i++) {
struct clk *clk;
+ int ret;
clk = of_clk_get(np, i);
if (IS_ERR(clk)) {
@@ -88,6 +77,29 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
simple->clks[i] = clk;
}
+ return 0;
+}
+
+static int dwc3_of_simple_probe(struct platform_device *pdev)
+{
+ struct dwc3_of_simple *simple;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ int ret;
+ int i;
+
+ simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
+ if (!simple)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, simple);
+ simple->dev = dev;
+
+ ret = dwc3_of_simple_clk_init(simple, of_clk_get_parent_count(np));
+ if (ret)
+ return ret;
+
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret) {
for (i = 0; i < simple->num_clocks; i++) {
@@ -112,7 +124,7 @@ static int dwc3_of_simple_remove(struct platform_device *pdev)
int i;
for (i = 0; i < simple->num_clocks; i++) {
- clk_unprepare(simple->clks[i]);
+ clk_disable_unprepare(simple->clks[i]);
clk_put(simple->clks[i]);
}
@@ -162,7 +174,9 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "qcom,dwc3" },
+ { .compatible = "rockchip,rk3399-dwc3" },
{ .compatible = "xlnx,zynqmp-dwc3" },
+ { .compatible = "cavium,octeon-7130-usb-uctl" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 122e64d..07cc892 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -174,15 +174,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
int status)
{
struct dwc3 *dwc = dep->dwc;
- int i;
- if (req->started) {
- i = 0;
- do {
- dwc3_ep_inc_deq(dep);
- } while(++i < req->request.num_mapped_sgs);
- req->started = false;
- }
+ req->started = false;
list_del(&req->list);
req->trb = NULL;
@@ -348,7 +341,8 @@ 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))
+ if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
+ (dwc->gadget.speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN;
memset(&params, 0, sizeof(params));
@@ -490,7 +484,8 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
params.param0 |= DWC3_DEPCFG_ACTION_INIT;
}
- params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
+ if (usb_endpoint_xfer_control(desc))
+ params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN;
if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc))
params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN;
@@ -764,6 +759,8 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
kfree(req);
}
+static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep);
+
/**
* dwc3_prepare_one_trb - setup one TRB from one request
* @dep: endpoint for which this request is prepared
@@ -771,15 +768,13 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep,
*/
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, dma_addr_t dma,
- unsigned length, unsigned last, unsigned chain, unsigned node)
+ unsigned length, unsigned chain, unsigned node)
{
struct dwc3_trb *trb;
- dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s",
+ dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s",
dep->name, req, (unsigned long long) dma,
- length, last ? " last" : "",
- chain ? " chain" : "");
-
+ length, chain ? " chain" : "");
trb = &dep->trb_pool[dep->trb_enqueue];
@@ -826,12 +821,10 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
/* always enable Continue on Short Packet */
trb->ctrl |= DWC3_TRB_CTRL_CSP;
- if (!req->request.no_interrupt && !chain)
+ if ((!req->request.no_interrupt && !chain) ||
+ (dwc3_calc_trbs_left(dep) == 0))
trb->ctrl |= DWC3_TRB_CTRL_IOC | DWC3_TRB_CTRL_ISP_IMI;
- if (last && !usb_endpoint_xfer_isoc(dep->endpoint.desc))
- trb->ctrl |= DWC3_TRB_CTRL_LST;
-
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
@@ -856,12 +849,12 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
*/
static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index)
{
- if (!index)
- index = DWC3_TRB_NUM - 2;
- else
- index = dep->trb_enqueue - 1;
+ u8 tmp = index;
- return &dep->trb_pool[index];
+ if (!tmp)
+ tmp = DWC3_TRB_NUM - 1;
+
+ return &dep->trb_pool[tmp - 1];
}
static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
@@ -894,65 +887,42 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
}
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
- struct dwc3_request *req, unsigned int trbs_left,
- unsigned int more_coming)
+ struct dwc3_request *req)
{
- struct usb_request *request = &req->request;
- struct scatterlist *sg = request->sg;
+ struct scatterlist *sg = req->sg;
struct scatterlist *s;
- unsigned int last = false;
unsigned int length;
dma_addr_t dma;
int i;
- for_each_sg(sg, s, request->num_mapped_sgs, i) {
+ for_each_sg(sg, s, req->num_pending_sgs, i) {
unsigned chain = true;
length = sg_dma_len(s);
dma = sg_dma_address(s);
- if (sg_is_last(s)) {
- if (usb_endpoint_xfer_int(dep->endpoint.desc) ||
- !more_coming)
- last = true;
-
- chain = false;
- }
-
- if (!trbs_left--)
- last = true;
-
- if (last)
+ if (sg_is_last(s))
chain = false;
dwc3_prepare_one_trb(dep, req, dma, length,
- last, chain, i);
+ chain, i);
- if (last)
+ if (!dwc3_calc_trbs_left(dep))
break;
}
}
static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
- struct dwc3_request *req, unsigned int trbs_left,
- unsigned int more_coming)
+ struct dwc3_request *req)
{
- unsigned int last = false;
unsigned int length;
dma_addr_t dma;
dma = req->request.dma;
length = req->request.length;
- if (!trbs_left)
- last = true;
-
- /* Is this the last request? */
- if (usb_endpoint_xfer_int(dep->endpoint.desc) || !more_coming)
- last = true;
-
dwc3_prepare_one_trb(dep, req, dma, length,
- last, false, 0);
+ false, 0);
}
/*
@@ -966,26 +936,19 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
static void dwc3_prepare_trbs(struct dwc3_ep *dep)
{
struct dwc3_request *req, *n;
- unsigned int more_coming;
- u32 trbs_left;
BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM);
- trbs_left = dwc3_calc_trbs_left(dep);
- if (!trbs_left)
+ if (!dwc3_calc_trbs_left(dep))
return;
- more_coming = dep->allocated_requests - dep->queued_requests;
-
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
- if (req->request.num_mapped_sgs > 0)
- dwc3_prepare_one_trb_sg(dep, req, trbs_left--,
- more_coming);
+ if (req->num_pending_sgs > 0)
+ dwc3_prepare_one_trb_sg(dep, req);
else
- dwc3_prepare_one_trb_linear(dep, req, trbs_left--,
- more_coming);
+ dwc3_prepare_one_trb_linear(dep, req);
- if (!trbs_left)
+ if (!dwc3_calc_trbs_left(dep))
return;
}
}
@@ -1101,93 +1064,29 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
trace_dwc3_ep_queue(req);
- /*
- * We only add to our list of requests now and
- * start consuming the list once we get XferNotReady
- * IRQ.
- *
- * That way, we avoid doing anything that we don't need
- * to do now and defer it until the point we receive a
- * particular token from the Host side.
- *
- * This will also avoid Host cancelling URBs due to too
- * many NAKs.
- */
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
dep->direction);
if (ret)
return ret;
- list_add_tail(&req->list, &dep->pending_list);
-
- /*
- * If there are no pending requests and the endpoint isn't already
- * busy, we will just start the request straight away.
- *
- * This will save one IRQ (XFER_NOT_READY) and possibly make it a
- * little bit faster.
- */
- if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- !usb_endpoint_xfer_int(dep->endpoint.desc)) {
- ret = __dwc3_gadget_kick_transfer(dep, 0);
- goto out;
- }
-
- /*
- * There are a few special cases:
- *
- * 1. XferNotReady with empty list of requests. We need to kick the
- * transfer here in that situation, otherwise we will be NAKing
- * forever. If we get XferNotReady before gadget driver has a
- * chance to queue a request, we will ACK the IRQ but won't be
- * able to receive the data until the next request is queued.
- * The following code is handling exactly that.
- *
- */
- if (dep->flags & DWC3_EP_PENDING_REQUEST) {
- /*
- * If xfernotready is already elapsed and it is a case
- * of isoc transfer, then issue END TRANSFER, so that
- * you can receive xfernotready again and can have
- * notion of current microframe.
- */
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
- if (list_empty(&dep->started_list)) {
- dwc3_stop_active_transfer(dwc, dep->number, true);
- dep->flags = DWC3_EP_ENABLED;
- }
- return 0;
- }
-
- ret = __dwc3_gadget_kick_transfer(dep, 0);
- if (!ret)
- dep->flags &= ~DWC3_EP_PENDING_REQUEST;
+ req->sg = req->request.sg;
+ req->num_pending_sgs = req->request.num_mapped_sgs;
- goto out;
- }
+ list_add_tail(&req->list, &dep->pending_list);
- /*
- * 2. XferInProgress on Isoc EP with an active transfer. We need to
- * kick the transfer here after queuing a request, otherwise the
- * core may not see the modified TRB(s).
- */
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (dep->flags & DWC3_EP_BUSY) &&
- !(dep->flags & DWC3_EP_MISSED_ISOC)) {
- WARN_ON_ONCE(!dep->resource_index);
- ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index);
- goto out;
+ dep->flags & DWC3_EP_PENDING_REQUEST) {
+ if (list_empty(&dep->started_list)) {
+ dwc3_stop_active_transfer(dwc, dep->number, true);
+ dep->flags = DWC3_EP_ENABLED;
+ }
+ return 0;
}
- /*
- * 4. Stream Capable Bulk Endpoints. We need to start the transfer
- * right away, otherwise host will not know we have streams to be
- * handled.
- */
- if (dep->stream_capable)
- ret = __dwc3_gadget_kick_transfer(dep, 0);
+ if (!dwc3_calc_trbs_left(dep))
+ return 0;
-out:
+ ret = __dwc3_gadget_kick_transfer(dep, 0);
if (ret && ret != -EBUSY)
dwc3_trace(trace_dwc3_gadget,
"%s: failed to kick transfers",
@@ -1963,6 +1862,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
unsigned int trb_status;
dep->queued_requests--;
+ dwc3_ep_inc_deq(dep);
trace_dwc3_complete_trb(dep, trb);
/*
@@ -1982,6 +1882,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1;
count = trb->size & DWC3_TRB_SIZE_MASK;
+ req->request.actual += count;
if (dep->direction) {
if (count) {
@@ -2021,48 +1922,51 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
if (s_pkt && !chain)
return 1;
- if ((event->status & DEPEVT_STATUS_LST) &&
- (trb->ctrl & (DWC3_TRB_CTRL_LST |
- DWC3_TRB_CTRL_HWO)))
- return 1;
+
if ((event->status & DEPEVT_STATUS_IOC) &&
(trb->ctrl & DWC3_TRB_CTRL_IOC))
return 1;
+
return 0;
}
static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
const struct dwc3_event_depevt *event, int status)
{
- struct dwc3_request *req;
+ struct dwc3_request *req, *n;
struct dwc3_trb *trb;
- unsigned int slot;
- unsigned int i;
- int count = 0;
+ bool ioc = false;
int ret;
- do {
+ list_for_each_entry_safe(req, n, &dep->started_list, list) {
+ unsigned length;
+ unsigned actual;
int chain;
- req = next_request(&dep->started_list);
- if (WARN_ON_ONCE(!req))
- return 1;
-
- chain = req->request.num_mapped_sgs > 0;
- i = 0;
- do {
- slot = req->first_trb_index + i;
- if (slot == DWC3_TRB_NUM - 1)
- slot++;
- slot %= DWC3_TRB_NUM;
- trb = &dep->trb_pool[slot];
- count += trb->size & DWC3_TRB_SIZE_MASK;
+ length = req->request.length;
+ chain = req->num_pending_sgs > 0;
+ if (chain) {
+ struct scatterlist *sg = req->sg;
+ struct scatterlist *s;
+ unsigned int pending = req->num_pending_sgs;
+ unsigned int i;
+ for_each_sg(sg, s, pending, i) {
+ trb = &dep->trb_pool[dep->trb_dequeue];
+
+ req->sg = sg_next(s);
+ req->num_pending_sgs--;
+
+ ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+ event, status, chain);
+ if (ret)
+ break;
+ }
+ } else {
+ trb = &dep->trb_pool[dep->trb_dequeue];
ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
event, status, chain);
- if (ret)
- break;
- } while (++i < req->request.num_mapped_sgs);
+ }
/*
* We assume here we will always receive the entire data block
@@ -2071,12 +1975,21 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
* should receive and we simply bounce the request back to the
* gadget driver for further processing.
*/
- req->request.actual += req->request.length - count;
+ actual = length - req->request.actual;
+ req->request.actual = actual;
+
+ if (ret && chain && (actual < length) && req->num_pending_sgs)
+ return __dwc3_gadget_kick_transfer(dep, 0);
+
dwc3_gadget_giveback(dep, req, status);
- if (ret)
+ if (ret) {
+ if ((event->status & DEPEVT_STATUS_IOC) &&
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ ioc = true;
break;
- } while (1);
+ }
+ }
/*
* Our endpoint might get disabled by another thread during
@@ -2103,10 +2016,9 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
return 1;
}
- if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
- return 0;
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && ioc)
+ return 0;
+
return 1;
}
@@ -2322,6 +2234,18 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
*
* - Issue EndTransfer WITH CMDIOC bit set
* - Wait 100us
+ *
+ * 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 and we don't need to delay for
+ * 100us.
+ *
+ * This mode is NOT available on the DWC_usb31 IP.
*/
cmd = DWC3_DEPCMD_ENDTRANSFER;
@@ -2333,7 +2257,9 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
WARN_ON_ONCE(ret);
dep->resource_index = 0;
dep->flags &= ~DWC3_EP_BUSY;
- udelay(100);
+
+ if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
+ udelay(100);
}
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c
index ec004c6..bd86f84 100644
--- a/drivers/usb/dwc3/ulpi.c
+++ b/drivers/usb/dwc3/ulpi.c
@@ -35,9 +35,9 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
return -ETIMEDOUT;
}
-static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr)
+static int dwc3_ulpi_read(struct device *dev, u8 addr)
{
- struct dwc3 *dwc = dev_get_drvdata(ops->dev);
+ struct dwc3 *dwc = dev_get_drvdata(dev);
u32 reg;
int ret;
@@ -53,9 +53,9 @@ static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr)
return DWC3_GUSB2PHYACC_DATA(reg);
}
-static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val)
+static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val)
{
- struct dwc3 *dwc = dev_get_drvdata(ops->dev);
+ struct dwc3 *dwc = dev_get_drvdata(dev);
u32 reg;
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
@@ -65,7 +65,7 @@ static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val)
return dwc3_ulpi_busyloop(dwc);
}
-static struct ulpi_ops dwc3_ulpi_ops = {
+static const struct ulpi_ops dwc3_ulpi_ops = {
.read = dwc3_ulpi_read,
.write = dwc3_ulpi_write,
};
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 3c3f31c..8ad2032 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -209,25 +209,6 @@ config USB_F_PRINTER
config USB_F_TCM
tristate
-choice
- tristate "USB Gadget Drivers"
- default USB_ETH
- help
- A Linux "Gadget Driver" talks to the USB Peripheral Controller
- driver through the abstract "gadget" API. Some other operating
- systems call these "client" drivers, of which "class drivers"
- are a subset (implementing a USB device class specification).
- A gadget driver implements one or more USB functions using
- the peripheral hardware.
-
- Gadget drivers are hardware-neutral, or "platform independent",
- except that they sometimes must understand quirks or limitations
- of the particular controllers they work with. For example, when
- a controller doesn't support alternate configurations or provide
- enough of the right types of endpoints, the gadget driver might
- not be able work with that controller, or might need to implement
- a less common variant of a device class protocol.
-
# this first set of drivers all depend on bulk-capable hardware.
config USB_CONFIGFS
@@ -439,6 +420,7 @@ config USB_CONFIGFS_F_HID
config USB_CONFIGFS_F_UVC
bool "USB Webcam function"
depends on USB_CONFIGFS
+ depends on VIDEO_V4L2
depends on VIDEO_DEV
select VIDEOBUF2_VMALLOC
select USB_F_UVC
@@ -475,6 +457,25 @@ config USB_CONFIGFS_F_TCM
Both protocols can work on USB2.0 and USB3.0.
UAS utilizes the USB 3.0 feature called streams support.
+choice
+ tristate "USB Gadget Drivers"
+ default USB_ETH
+ help
+ A Linux "Gadget Driver" talks to the USB Peripheral Controller
+ driver through the abstract "gadget" API. Some other operating
+ systems call these "client" drivers, of which "class drivers"
+ are a subset (implementing a USB device class specification).
+ A gadget driver implements one or more USB functions using
+ the peripheral hardware.
+
+ Gadget drivers are hardware-neutral, or "platform independent",
+ except that they sometimes must understand quirks or limitations
+ of the particular controllers they work with. For example, when
+ a controller doesn't support alternate configurations or provide
+ enough of the right types of endpoints, the gadget driver might
+ not be able work with that controller, or might need to implement
+ a less common variant of a device class protocol.
+
source "drivers/usb/gadget/legacy/Kconfig"
endchoice
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 5ebe6af7..32176f7 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1893,17 +1893,21 @@ 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))
+ if (f->req_match &&
+ f->req_match(f, ctrl, false))
goto try_fun_setup;
- f = NULL;
+ } 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:
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index f9237fe..3984787 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1211,8 +1211,9 @@ static void purge_configs_funcs(struct gadget_info *gi)
list_move_tail(&f->list, &cfg->func_list);
if (f->unbind) {
- dev_err(&gi->cdev.gadget->dev, "unbind function"
- " '%s'/%p\n", f->name, f);
+ dev_dbg(&gi->cdev.gadget->dev,
+ "unbind function '%s'/%p\n",
+ f->name, f);
f->unbind(c, f);
}
}
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 5c8429f..0aeed85 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -98,6 +98,9 @@ static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
static void ffs_func_disable(struct usb_function *);
static int ffs_func_setup(struct usb_function *,
const struct usb_ctrlrequest *);
+static bool ffs_func_req_match(struct usb_function *,
+ const struct usb_ctrlrequest *,
+ bool config0);
static void ffs_func_suspend(struct usb_function *);
static void ffs_func_resume(struct usb_function *);
@@ -2243,7 +2246,9 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
FUNCTIONFS_HAS_SS_DESC |
FUNCTIONFS_HAS_MS_OS_DESC |
FUNCTIONFS_VIRTUAL_ADDR |
- FUNCTIONFS_EVENTFD)) {
+ FUNCTIONFS_EVENTFD |
+ FUNCTIONFS_ALL_CTRL_RECIP |
+ FUNCTIONFS_CONFIG0_SETUP)) {
ret = -ENOSYS;
goto error;
}
@@ -3094,8 +3099,9 @@ static int ffs_func_setup(struct usb_function *f,
* handle them. All other either handled by composite or
* passed to usb_configuration->setup() (if one is set). No
* matter, we will handle requests directed to endpoint here
- * as well (as it's straightforward) but what to do with any
- * other request?
+ * as well (as it's straightforward). Other request recipient
+ * types are only handled when the user flag FUNCTIONFS_ALL_CTRL_RECIP
+ * is being used.
*/
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
@@ -3116,7 +3122,10 @@ static int ffs_func_setup(struct usb_function *f,
break;
default:
- return -EOPNOTSUPP;
+ if (func->ffs->user_flags & FUNCTIONFS_ALL_CTRL_RECIP)
+ ret = le16_to_cpu(creq->wIndex);
+ else
+ return -EOPNOTSUPP;
}
spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
@@ -3128,6 +3137,28 @@ static int ffs_func_setup(struct usb_function *f,
return 0;
}
+static bool ffs_func_req_match(struct usb_function *f,
+ const struct usb_ctrlrequest *creq,
+ bool config0)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+
+ if (config0 && !(func->ffs->user_flags & FUNCTIONFS_CONFIG0_SETUP))
+ return false;
+
+ switch (creq->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ return ffs_func_revmap_intf(func,
+ le16_to_cpu(creq->wIndex) >= 0);
+ case USB_RECIP_ENDPOINT:
+ return ffs_func_revmap_ep(func,
+ le16_to_cpu(creq->wIndex) >= 0);
+ default:
+ return (bool) (func->ffs->user_flags &
+ FUNCTIONFS_ALL_CTRL_RECIP);
+ }
+}
+
static void ffs_func_suspend(struct usb_function *f)
{
ENTER();
@@ -3378,6 +3409,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
func->function.set_alt = ffs_func_set_alt;
func->function.disable = ffs_func_disable;
func->function.setup = ffs_func_setup;
+ func->function.req_match = ffs_func_req_match;
func->function.suspend = ffs_func_suspend;
func->function.resume = ffs_func_resume;
func->function.free_func = ffs_free;
@@ -3470,6 +3502,11 @@ static void _ffs_free_dev(struct ffs_dev *dev)
list_del(&dev->entry);
if (dev->name_allocated)
kfree(dev->name);
+
+ /* Clear the private_data pointer to stop incorrect dev access */
+ if (dev->ffs_data)
+ dev->ffs_data->private_data = NULL;
+
kfree(dev);
if (list_empty(&ffs_devices))
functionfs_cleanup();
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 51980c5..e2966f8 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -365,7 +365,7 @@ static int f_hidg_open(struct inode *inode, struct file *fd)
static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
unsigned length)
{
- return alloc_ep_req(ep, length, length);
+ return alloc_ep_req(ep, length);
}
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
@@ -617,14 +617,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
/* preallocate request and buffer */
status = -ENOMEM;
- hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
+ hidg->req = alloc_ep_req(hidg->in_ep, hidg->report_length);
if (!hidg->req)
goto fail;
- hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
- if (!hidg->req->buf)
- goto fail;
-
/* set descriptor dynamic values */
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
@@ -677,11 +673,8 @@ fail_free_descs:
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
- if (hidg->req != NULL) {
- kfree(hidg->req->buf);
- if (hidg->in_ep != NULL)
- usb_ep_free_request(hidg->in_ep, hidg->req);
- }
+ if (hidg->req != NULL)
+ free_ep_req(hidg->in_ep, hidg->req);
return status;
}
@@ -809,11 +802,21 @@ end:
CONFIGFS_ATTR(f_hid_opts_, report_desc);
+static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page)
+{
+ struct f_hid_opts *opts = to_f_hid_opts(item);
+
+ return sprintf(page, "%d:%d\n", major, opts->minor);
+}
+
+CONFIGFS_ATTR_RO(f_hid_opts_, dev);
+
static struct configfs_attribute *hid_attrs[] = {
&f_hid_opts_attr_subclass,
&f_hid_opts_attr_protocol,
&f_hid_opts_attr_report_length,
&f_hid_opts_attr_report_desc,
+ &f_hid_opts_attr_dev,
NULL,
};
@@ -910,8 +913,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
/* disable/free request and end point */
usb_ep_disable(hidg->in_ep);
- kfree(hidg->req->buf);
- usb_ep_free_request(hidg->in_ep, hidg->req);
+ free_ep_req(hidg->in_ep, hidg->req);
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 3a9f8f9..e700938 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -308,9 +308,7 @@ static void disable_loopback(struct f_loopback *loop)
static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
{
- struct f_loopback *loop = ep->driver_data;
-
- return alloc_ep_req(ep, len, loop->buflen);
+ return alloc_ep_req(ep, len);
}
static int alloc_requests(struct usb_composite_dev *cdev,
@@ -333,7 +331,7 @@ static int alloc_requests(struct usb_composite_dev *cdev,
if (!in_req)
goto fail;
- out_req = lb_alloc_ep_req(loop->out_ep, 0);
+ out_req = lb_alloc_ep_req(loop->out_ep, loop->buflen);
if (!out_req)
goto fail_in;
@@ -593,13 +591,9 @@ DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
int __init lb_modinit(void)
{
- int ret;
-
- ret = usb_function_register(&Loopbackusb_func);
- if (ret)
- return ret;
- return ret;
+ return usb_function_register(&Loopbackusb_func);
}
+
void __exit lb_modexit(void)
{
usb_function_unregister(&Loopbackusb_func);
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 2505117..8f3659b 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -311,11 +311,7 @@ struct fsg_common {
/* Gadget's private data. */
void *private_data;
- /*
- * Vendor (8 chars), product (16 chars), release (4
- * hexadecimal digits) and NUL byte
- */
- char inquiry_string[8 + 16 + 4 + 1];
+ char inquiry_string[INQUIRY_STRING_LEN];
struct kref ref;
};
@@ -1107,7 +1103,12 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
buf[5] = 0; /* No special options */
buf[6] = 0;
buf[7] = 0;
- memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string);
+ if (curlun->inquiry_string[0])
+ memcpy(buf + 8, curlun->inquiry_string,
+ sizeof(curlun->inquiry_string));
+ else
+ memcpy(buf + 8, common->inquiry_string,
+ sizeof(common->inquiry_string));
return 36;
}
@@ -3209,12 +3210,27 @@ static ssize_t fsg_lun_opts_nofua_store(struct config_item *item,
CONFIGFS_ATTR(fsg_lun_opts_, nofua);
+static ssize_t fsg_lun_opts_inquiry_string_show(struct config_item *item,
+ char *page)
+{
+ return fsg_show_inquiry_string(to_fsg_lun_opts(item)->lun, page);
+}
+
+static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item,
+ const char *page, size_t len)
+{
+ return fsg_store_inquiry_string(to_fsg_lun_opts(item)->lun, page, len);
+}
+
+CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string);
+
static struct configfs_attribute *fsg_lun_attrs[] = {
&fsg_lun_opts_attr_file,
&fsg_lun_opts_attr_ro,
&fsg_lun_opts_attr_removable,
&fsg_lun_opts_attr_cdrom,
&fsg_lun_opts_attr_nofua,
+ &fsg_lun_opts_attr_inquiry_string,
NULL,
};
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index b6a9918..d390231 100644
--- a/drivers/usb/gadget/function/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -100,6 +100,7 @@ struct fsg_lun_config {
char removable;
char cdrom;
char nofua;
+ char inquiry_string[INQUIRY_STRING_LEN];
};
struct fsg_config {
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 58fc199..a5719f2 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -51,6 +51,19 @@ static const char f_midi_longname[] = "MIDI Gadget";
*/
#define MAX_PORTS 16
+/* MIDI message states */
+enum {
+ STATE_INITIAL = 0, /* pseudo state */
+ STATE_1PARAM,
+ STATE_2PARAM_1,
+ STATE_2PARAM_2,
+ STATE_SYSEX_0,
+ STATE_SYSEX_1,
+ STATE_SYSEX_2,
+ STATE_REAL_TIME,
+ STATE_FINISHED, /* pseudo state */
+};
+
/*
* This is a gadget, and the IN/OUT naming is from the host's perspective.
* USB -> OUT endpoint -> rawmidi
@@ -61,13 +74,6 @@ struct gmidi_in_port {
int active;
uint8_t cable;
uint8_t state;
-#define STATE_UNKNOWN 0
-#define STATE_1PARAM 1
-#define STATE_2PARAM_1 2
-#define STATE_2PARAM_2 3
-#define STATE_SYSEX_0 4
-#define STATE_SYSEX_1 5
-#define STATE_SYSEX_2 6
uint8_t data[2];
};
@@ -205,7 +211,7 @@ static struct usb_gadget_strings *midi_strings[] = {
static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
unsigned length)
{
- return alloc_ep_req(ep, length, length);
+ return alloc_ep_req(ep, length);
}
static const uint8_t f_midi_cin_length[] = {
@@ -299,6 +305,19 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req)
}
}
+static void f_midi_drop_out_substreams(struct f_midi *midi)
+{
+ unsigned int i;
+
+ for (i = 0; i < midi->in_ports; i++) {
+ struct gmidi_in_port *port = midi->in_ports_array + i;
+ struct snd_rawmidi_substream *substream = port->substream;
+
+ if (port->active && substream)
+ snd_rawmidi_drop_output(substream);
+ }
+}
+
static int f_midi_start_ep(struct f_midi *midi,
struct usb_function *f,
struct usb_ep *ep)
@@ -360,9 +379,8 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
/* allocate a bunch of read buffers and queue them all at once. */
for (i = 0; i < midi->qlen && err == 0; i++) {
struct usb_request *req =
- midi_alloc_ep_req(midi->out_ep,
- max_t(unsigned, midi->buflen,
- bulk_out_desc.wMaxPacketSize));
+ midi_alloc_ep_req(midi->out_ep, midi->buflen);
+
if (req == NULL)
return -ENOMEM;
@@ -397,6 +415,8 @@ static void f_midi_disable(struct usb_function *f)
/* release IN requests */
while (kfifo_get(&midi->in_req_fifo, &req))
free_ep_req(midi->in_ep, req);
+
+ f_midi_drop_out_substreams(midi);
}
static int f_midi_snd_free(struct snd_device *device)
@@ -404,130 +424,166 @@ static int f_midi_snd_free(struct snd_device *device)
return 0;
}
-static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0,
- uint8_t p1, uint8_t p2, uint8_t p3)
-{
- unsigned length = req->length;
- u8 *buf = (u8 *)req->buf + length;
-
- buf[0] = p0;
- buf[1] = p1;
- buf[2] = p2;
- buf[3] = p3;
- req->length = length + 4;
-}
-
/*
* Converts MIDI commands to USB MIDI packets.
*/
static void f_midi_transmit_byte(struct usb_request *req,
struct gmidi_in_port *port, uint8_t b)
{
- uint8_t p0 = port->cable << 4;
+ uint8_t p[4] = { port->cable << 4, 0, 0, 0 };
+ uint8_t next_state = STATE_INITIAL;
+
+ switch (b) {
+ case 0xf8 ... 0xff:
+ /* System Real-Time Messages */
+ p[0] |= 0x0f;
+ p[1] = b;
+ next_state = port->state;
+ port->state = STATE_REAL_TIME;
+ break;
- if (b >= 0xf8) {
- f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
- } else if (b >= 0xf0) {
+ case 0xf7:
+ /* End of SysEx */
+ switch (port->state) {
+ case STATE_SYSEX_0:
+ p[0] |= 0x05;
+ p[1] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ case STATE_SYSEX_1:
+ p[0] |= 0x06;
+ p[1] = port->data[0];
+ p[2] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ case STATE_SYSEX_2:
+ p[0] |= 0x07;
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = 0xf7;
+ next_state = STATE_FINISHED;
+ break;
+ default:
+ /* Ignore byte */
+ next_state = port->state;
+ port->state = STATE_INITIAL;
+ }
+ break;
+
+ case 0xf0 ... 0xf6:
+ /* System Common Messages */
+ port->data[0] = port->data[1] = 0;
+ port->state = STATE_INITIAL;
switch (b) {
case 0xf0:
port->data[0] = b;
- port->state = STATE_SYSEX_1;
+ port->data[1] = 0;
+ next_state = STATE_SYSEX_1;
break;
case 0xf1:
case 0xf3:
port->data[0] = b;
- port->state = STATE_1PARAM;
+ next_state = STATE_1PARAM;
break;
case 0xf2:
port->data[0] = b;
- port->state = STATE_2PARAM_1;
+ next_state = STATE_2PARAM_1;
break;
case 0xf4:
case 0xf5:
- port->state = STATE_UNKNOWN;
+ next_state = STATE_INITIAL;
break;
case 0xf6:
- f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
- port->state = STATE_UNKNOWN;
- break;
- case 0xf7:
- switch (port->state) {
- case STATE_SYSEX_0:
- f_midi_transmit_packet(req,
- p0 | 0x05, 0xf7, 0, 0);
- break;
- case STATE_SYSEX_1:
- f_midi_transmit_packet(req,
- p0 | 0x06, port->data[0], 0xf7, 0);
- break;
- case STATE_SYSEX_2:
- f_midi_transmit_packet(req,
- p0 | 0x07, port->data[0],
- port->data[1], 0xf7);
- break;
- }
- port->state = STATE_UNKNOWN;
+ p[0] |= 0x05;
+ p[1] = 0xf6;
+ next_state = STATE_FINISHED;
break;
}
- } else if (b >= 0x80) {
+ break;
+
+ case 0x80 ... 0xef:
+ /*
+ * Channel Voice Messages, Channel Mode Messages
+ * and Control Change Messages.
+ */
port->data[0] = b;
+ port->data[1] = 0;
+ port->state = STATE_INITIAL;
if (b >= 0xc0 && b <= 0xdf)
- port->state = STATE_1PARAM;
+ next_state = STATE_1PARAM;
else
- port->state = STATE_2PARAM_1;
- } else { /* b < 0x80 */
+ next_state = STATE_2PARAM_1;
+ break;
+
+ case 0x00 ... 0x7f:
+ /* Message parameters */
switch (port->state) {
case STATE_1PARAM:
- if (port->data[0] < 0xf0) {
- p0 |= port->data[0] >> 4;
- } else {
- p0 |= 0x02;
- port->state = STATE_UNKNOWN;
- }
- f_midi_transmit_packet(req, p0, port->data[0], b, 0);
+ if (port->data[0] < 0xf0)
+ p[0] |= port->data[0] >> 4;
+ else
+ p[0] |= 0x02;
+
+ p[1] = port->data[0];
+ p[2] = b;
+ /* This is to allow Running State Messages */
+ next_state = STATE_1PARAM;
break;
case STATE_2PARAM_1:
port->data[1] = b;
- port->state = STATE_2PARAM_2;
+ next_state = STATE_2PARAM_2;
break;
case STATE_2PARAM_2:
- if (port->data[0] < 0xf0) {
- p0 |= port->data[0] >> 4;
- port->state = STATE_2PARAM_1;
- } else {
- p0 |= 0x03;
- port->state = STATE_UNKNOWN;
- }
- f_midi_transmit_packet(req,
- p0, port->data[0], port->data[1], b);
+ if (port->data[0] < 0xf0)
+ p[0] |= port->data[0] >> 4;
+ else
+ p[0] |= 0x03;
+
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = b;
+ /* This is to allow Running State Messages */
+ next_state = STATE_2PARAM_1;
break;
case STATE_SYSEX_0:
port->data[0] = b;
- port->state = STATE_SYSEX_1;
+ next_state = STATE_SYSEX_1;
break;
case STATE_SYSEX_1:
port->data[1] = b;
- port->state = STATE_SYSEX_2;
+ next_state = STATE_SYSEX_2;
break;
case STATE_SYSEX_2:
- f_midi_transmit_packet(req,
- p0 | 0x04, port->data[0], port->data[1], b);
- port->state = STATE_SYSEX_0;
+ p[0] |= 0x04;
+ p[1] = port->data[0];
+ p[2] = port->data[1];
+ p[3] = b;
+ next_state = STATE_SYSEX_0;
break;
}
+ break;
}
-}
-static void f_midi_drop_out_substreams(struct f_midi *midi)
-{
- unsigned int i;
+ /* States where we have to write into the USB request */
+ if (next_state == STATE_FINISHED ||
+ port->state == STATE_SYSEX_2 ||
+ port->state == STATE_1PARAM ||
+ port->state == STATE_2PARAM_2 ||
+ port->state == STATE_REAL_TIME) {
- for (i = 0; i < midi->in_ports; i++) {
- struct gmidi_in_port *port = midi->in_ports_array + i;
- struct snd_rawmidi_substream *substream = port->substream;
- if (port->active && substream)
- snd_rawmidi_drop_output(substream);
+ unsigned int length = req->length;
+ u8 *buf = (u8 *)req->buf + length;
+
+ memcpy(buf, p, sizeof(p));
+ req->length = length + sizeof(p);
+
+ if (next_state == STATE_FINISHED) {
+ next_state = STATE_INITIAL;
+ port->data[0] = port->data[1] = 0;
+ }
}
+
+ port->state = next_state;
}
static int f_midi_do_transmit(struct f_midi *midi, struct usb_ep *ep)
@@ -642,7 +698,7 @@ static int f_midi_in_open(struct snd_rawmidi_substream *substream)
VDBG(midi, "%s()\n", __func__);
port = midi->in_ports_array + substream->number;
port->substream = substream;
- port->state = STATE_UNKNOWN;
+ port->state = STATE_INITIAL;
return 0;
}
@@ -1123,7 +1179,7 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
opts->func_inst.free_func_inst = f_midi_free_inst;
opts->index = SNDRV_DEFAULT_IDX1;
opts->id = SNDRV_DEFAULT_STR1;
- opts->buflen = 256;
+ opts->buflen = 512;
opts->qlen = 32;
opts->in_ports = 1;
opts->out_ports = 1;
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 97f0a9b..6396037 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -90,7 +90,9 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
/* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ncm_bitrate(struct usb_gadget *g)
{
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return 13 * 1024 * 8 * 1000 * 8;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8;
else
return 19 * 64 * 1 * 1000 * 8;
@@ -333,6 +335,76 @@ static struct usb_descriptor_header *ncm_hs_function[] = {
NULL,
};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_ncm_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
+ .bInterval = USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS)
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ncm_notify_comp_desc = {
+ .bLength = sizeof(ss_ncm_notify_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(NCM_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_ncm_in_desc = {
+ .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_ncm_out_desc = {
+ .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 ss_ncm_bulk_comp_desc = {
+ .bLength = sizeof(ss_ncm_bulk_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *ncm_ss_function[] = {
+ (struct usb_descriptor_header *) &ncm_iad_desc,
+ /* CDC NCM control descriptors */
+ (struct usb_descriptor_header *) &ncm_control_intf,
+ (struct usb_descriptor_header *) &ncm_header_desc,
+ (struct usb_descriptor_header *) &ncm_union_desc,
+ (struct usb_descriptor_header *) &ecm_desc,
+ (struct usb_descriptor_header *) &ncm_desc,
+ (struct usb_descriptor_header *) &ss_ncm_notify_desc,
+ (struct usb_descriptor_header *) &ss_ncm_notify_comp_desc,
+ /* data interface, altsettings 0 and 1 */
+ (struct usb_descriptor_header *) &ncm_data_nop_intf,
+ (struct usb_descriptor_header *) &ncm_data_intf,
+ (struct usb_descriptor_header *) &ss_ncm_in_desc,
+ (struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
+ (struct usb_descriptor_header *) &ss_ncm_out_desc,
+ (struct usb_descriptor_header *) &ss_ncm_bulk_comp_desc,
+ NULL,
+};
+
/* string descriptors: */
#define STRING_CTRL_IDX 0
@@ -852,6 +924,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
*/
ncm->port.is_zlp_ok =
gadget_is_zlp_supported(cdev->gadget);
+ ncm->port.no_skb_reserve =
+ gadget_avoids_skb_reserve(cdev->gadget);
ncm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ncm\n");
net = gether_connect(&ncm->port);
@@ -1431,8 +1505,13 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
hs_ncm_notify_desc.bEndpointAddress =
fs_ncm_notify_desc.bEndpointAddress;
+ ss_ncm_in_desc.bEndpointAddress = fs_ncm_in_desc.bEndpointAddress;
+ ss_ncm_out_desc.bEndpointAddress = fs_ncm_out_desc.bEndpointAddress;
+ ss_ncm_notify_desc.bEndpointAddress =
+ fs_ncm_notify_desc.bEndpointAddress;
+
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
- NULL, NULL);
+ ncm_ss_function, NULL);
if (status)
goto fail;
@@ -1450,6 +1529,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm->task_timer.function = ncm_tx_timeout;
DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
ncm->port.in_ep->name, ncm->port.out_ep->name,
ncm->notify->name);
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 64706a7..0de36cd 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -889,13 +889,17 @@ static void printer_soft_reset(struct printer_dev *dev)
/*-------------------------------------------------------------------------*/
static bool gprinter_req_match(struct usb_function *f,
- const struct usb_ctrlrequest *ctrl)
+ const struct usb_ctrlrequest *ctrl,
+ bool config0)
{
struct printer_dev *dev = func_to_printer(f);
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
+ if (config0)
+ return false;
+
if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE ||
(ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
return false;
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index df0189d..8784fa1 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -293,9 +293,7 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
{
- struct f_sourcesink *ss = ep->driver_data;
-
- return alloc_ep_req(ep, len, ss->buflen);
+ return alloc_ep_req(ep, len);
}
static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
@@ -606,7 +604,7 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
} else {
ep = is_in ? ss->in_ep : ss->out_ep;
qlen = ss->bulk_qlen;
- size = 0;
+ size = ss->buflen;
}
for (i = 0; i < qlen; i++) {
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 29b41b5..27ed51b 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -258,6 +258,13 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
v4l2_event_queue(&uvc->vdev, &v4l2_event);
+ /* Pass additional setup data to userspace */
+ if (uvc->event_setup_out && uvc->event_length) {
+ uvc->control_req->length = uvc->event_length;
+ return usb_ep_queue(uvc->func.config->cdev->gadget->ep0,
+ uvc->control_req, GFP_ATOMIC);
+ }
+
return 0;
}
diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c
index 990df22..8fbf686 100644
--- a/drivers/usb/gadget/function/storage_common.c
+++ b/drivers/usb/gadget/function/storage_common.c
@@ -369,6 +369,12 @@ ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
}
EXPORT_SYMBOL_GPL(fsg_show_removable);
+ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf)
+{
+ return sprintf(buf, "%s\n", curlun->inquiry_string);
+}
+EXPORT_SYMBOL_GPL(fsg_show_inquiry_string);
+
/*
* The caller must hold fsg->filesem for reading when calling this function.
*/
@@ -499,4 +505,22 @@ ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
}
EXPORT_SYMBOL_GPL(fsg_store_removable);
+ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
+ size_t count)
+{
+ const size_t len = min(count, sizeof(curlun->inquiry_string));
+
+ if (len == 0 || buf[0] == '\n') {
+ curlun->inquiry_string[0] = 0;
+ } else {
+ snprintf(curlun->inquiry_string,
+ sizeof(curlun->inquiry_string), "%-28s", buf);
+ if (curlun->inquiry_string[len-1] == '\n')
+ curlun->inquiry_string[len-1] = ' ';
+ }
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(fsg_store_inquiry_string);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index c3544e6..e698489 100644
--- a/drivers/usb/gadget/function/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
@@ -88,6 +88,12 @@ do { \
#define ASC(x) ((u8) ((x) >> 8))
#define ASCQ(x) ((u8) (x))
+/*
+ * Vendor (8 chars), product (16 chars), release (4 hexadecimal digits) and NUL
+ * byte
+ */
+#define INQUIRY_STRING_LEN ((size_t) (8 + 16 + 4 + 1))
+
struct fsg_lun {
struct file *filp;
loff_t file_length;
@@ -112,6 +118,7 @@ struct fsg_lun {
struct device dev;
const char *name; /* "lun.name" */
const char **name_pfx; /* "function.name" */
+ char inquiry_string[INQUIRY_STRING_LEN];
};
static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
@@ -210,6 +217,7 @@ ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
char *buf);
+ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
@@ -221,5 +229,7 @@ ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
size_t count);
+ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
+ size_t count);
#endif /* USB_STORAGE_COMMON_H */
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 5f562c1..9c8c9ed 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -82,6 +82,7 @@ struct eth_dev {
#define WORK_RX_MEMORY 0
bool zlp;
+ bool no_skb_reserve;
u8 host_mac[ETH_ALEN];
u8 dev_mac[ETH_ALEN];
};
@@ -233,7 +234,8 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
* but on at least one, checksumming fails otherwise. Note:
* RNDIS headers involve variable numbers of LE32 values.
*/
- skb_reserve(skb, NET_IP_ALIGN);
+ if (likely(!dev->no_skb_reserve))
+ skb_reserve(skb, NET_IP_ALIGN);
req->buf = skb->data;
req->length = size;
@@ -569,7 +571,8 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
req->complete = tx_complete;
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
- if (dev->port_usb->is_fixed &&
+ if (dev->port_usb &&
+ dev->port_usb->is_fixed &&
length == dev->port_usb->fixed_in_len &&
(length % in->maxpacket) == 0)
req->zero = 0;
@@ -1063,6 +1066,7 @@ struct net_device *gether_connect(struct gether *link)
if (result == 0) {
dev->zlp = link->is_zlp_ok;
+ dev->no_skb_reserve = link->no_skb_reserve;
DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult));
dev->header_len = link->header_len;
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index c77145b..81d94a7 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -64,6 +64,7 @@ struct gether {
struct usb_ep *out_ep;
bool is_zlp_ok;
+ bool no_skb_reserve;
u16 cdc_filter;
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index fc2ac15..0bf39c3 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -47,7 +47,7 @@ static char *id = SNDRV_DEFAULT_STR1;
module_param(id, charp, S_IRUGO);
MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
-static unsigned int buflen = 256;
+static unsigned int buflen = 512;
module_param(buflen, uint, S_IRUGO);
MODULE_PARM_DESC(buflen, "MIDI buffer length");
diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c
index 4bc7eea..1883973 100644
--- a/drivers/usb/gadget/u_f.c
+++ b/drivers/usb/gadget/u_f.c
@@ -12,14 +12,16 @@
*/
#include "u_f.h"
+#include <linux/usb/ch9.h>
-struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
+struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len)
{
struct usb_request *req;
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (req) {
- req->length = len ?: default_len;
+ req->length = usb_endpoint_dir_out(ep->desc) ?
+ usb_ep_align(ep, len) : len;
req->buf = kmalloc(req->length, GFP_ATOMIC);
if (!req->buf) {
usb_ep_free_request(ep, req);
diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h
index 4247cc0..7d53a47 100644
--- a/drivers/usb/gadget/u_f.h
+++ b/drivers/usb/gadget/u_f.h
@@ -47,8 +47,21 @@
struct usb_ep;
struct usb_request;
-/* Requests allocated via alloc_ep_req() must be freed by free_ep_req(). */
-struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len);
+/**
+ * alloc_ep_req - returns a usb_request allocated by the gadget driver and
+ * allocates the request's buffer.
+ *
+ * @ep: the endpoint to allocate a usb_request
+ * @len: usb_requests's buffer suggested size
+ *
+ * In case @ep direction is OUT, the @len will be aligned to ep's
+ * wMaxPacketSize. In order to avoid memory leaks or drops, *always* use
+ * usb_requests's length (req->length) to refer to the allocated buffer size.
+ * Requests allocated via alloc_ep_req() *must* be freed by free_ep_req().
+ */
+struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len);
+
+/* Frees a usb_request previously allocated by alloc_ep_req() */
static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req)
{
kfree(req->buf);
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 40c04bb..9483489 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -107,10 +107,8 @@ int usb_ep_enable(struct usb_ep *ep)
goto out;
ret = ep->ops->enable(ep, ep->desc);
- if (ret) {
- ret = ret;
+ if (ret)
goto out;
- }
ep->enabled = true;
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index 8bb011e..4fff51b 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -421,10 +421,8 @@ static int qe_ep_rxbd_update(struct qe_ep *ep)
bd = ep->rxbase;
ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_ATOMIC);
- if (ep->rxframe == NULL) {
- dev_err(ep->udc->dev, "malloc rxframe failed\n");
+ if (!ep->rxframe)
return -ENOMEM;
- }
qe_frame_init(ep->rxframe);
@@ -435,9 +433,7 @@ static int qe_ep_rxbd_update(struct qe_ep *ep)
size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1);
ep->rxbuffer = kzalloc(size, GFP_ATOMIC);
- if (ep->rxbuffer == NULL) {
- dev_err(ep->udc->dev, "malloc rxbuffer failed,size=%d\n",
- size);
+ if (!ep->rxbuffer) {
kfree(ep->rxframe);
return -ENOMEM;
}
@@ -668,10 +664,8 @@ static int qe_ep_init(struct qe_udc *udc,
if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) {
ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_ATOMIC);
- if (ep->txframe == NULL) {
- dev_err(udc->dev, "malloc txframe failed\n");
+ if (!ep->txframe)
goto en_done2;
- }
qe_frame_init(ep->txframe);
}
@@ -2344,10 +2338,8 @@ static struct qe_udc *qe_udc_config(struct platform_device *ofdev)
u32 offset;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
- if (udc == NULL) {
- dev_err(&ofdev->dev, "malloc udc failed\n");
+ if (!udc)
goto cleanup;
- }
udc->dev = &ofdev->dev;
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index d2205d9..5107987 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -1767,8 +1767,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* alloc, and start init */
dev = kzalloc (sizeof *dev, GFP_KERNEL);
- if (dev == NULL){
- pr_debug("enomem %s\n", pci_name(pdev));
+ if (!dev) {
retval = -ENOMEM;
goto err;
}
@@ -1839,6 +1838,8 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
err:
if (dev)
goku_remove (pdev);
+ /* gadget_release is not registered yet, kfree explicitly */
+ kfree(dev);
return retval;
}
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index 614ab951..61c938c 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -589,7 +589,7 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep || !_req) {
- dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n",
+ dev_err(&ep->dev->pdev->dev, "%s: Invalid ep=%p or req=%p\n",
__func__, _ep, _req);
return;
}
@@ -1137,8 +1137,10 @@ dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount,
done(ep, req, status);
}
-static void scan_dma_completions(struct net2280_ep *ep)
+static int scan_dma_completions(struct net2280_ep *ep)
{
+ int num_completed = 0;
+
/* only look at descriptors that were "naturally" retired,
* so fifo and list head state won't matter
*/
@@ -1166,6 +1168,7 @@ static void scan_dma_completions(struct net2280_ep *ep)
break;
/* single transfer mode */
dma_done(ep, req, tmp, 0);
+ num_completed++;
break;
} else if (!ep->is_in &&
(req->req.length % ep->ep.maxpacket) &&
@@ -1194,7 +1197,10 @@ static void scan_dma_completions(struct net2280_ep *ep)
}
}
dma_done(ep, req, tmp, 0);
+ num_completed++;
}
+
+ return num_completed;
}
static void restart_dma(struct net2280_ep *ep)
@@ -1567,6 +1573,44 @@ static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget,
return ep;
}
+ /* USB3380: Only first four endpoints have DMA channels. Allocate
+ * slower interrupt endpoints from PIO hw endpoints, to allow bulk/isoc
+ * endpoints use DMA hw endpoints.
+ */
+ if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
+ usb_endpoint_dir_in(desc)) {
+ ep = gadget_find_ep_by_name(_gadget, "ep2in");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ ep = gadget_find_ep_by_name(_gadget, "ep4in");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ } else if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT &&
+ !usb_endpoint_dir_in(desc)) {
+ ep = gadget_find_ep_by_name(_gadget, "ep1out");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ ep = gadget_find_ep_by_name(_gadget, "ep3out");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK &&
+ usb_endpoint_dir_in(desc)) {
+ ep = gadget_find_ep_by_name(_gadget, "ep1in");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ ep = gadget_find_ep_by_name(_gadget, "ep3in");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK &&
+ !usb_endpoint_dir_in(desc)) {
+ ep = gadget_find_ep_by_name(_gadget, "ep2out");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ ep = gadget_find_ep_by_name(_gadget, "ep4out");
+ if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp))
+ return ep;
+ }
+
/* USB3380: use same address for usb and hardware endpoints */
snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc),
usb_endpoint_dir_in(desc) ? "in" : "out");
@@ -2547,8 +2591,11 @@ static void handle_ep_small(struct net2280_ep *ep)
/* manual DMA queue advance after short OUT */
if (likely(ep->dma)) {
if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
- u32 count;
+ struct net2280_request *stuck_req = NULL;
int stopped = ep->stopped;
+ int num_completed;
+ int stuck = 0;
+ u32 count;
/* TRANSFERRED works around OUT_DONE erratum 0112.
* we expect (N <= maxpacket) bytes; host wrote M.
@@ -2560,7 +2607,7 @@ static void handle_ep_small(struct net2280_ep *ep)
/* any preceding dma transfers must finish.
* dma handles (M >= N), may empty the queue
*/
- scan_dma_completions(ep);
+ num_completed = scan_dma_completions(ep);
if (unlikely(list_empty(&ep->queue) ||
ep->out_overflow)) {
req = NULL;
@@ -2580,6 +2627,31 @@ static void handle_ep_small(struct net2280_ep *ep)
req = NULL;
break;
}
+
+ /* Escape loop if no dma transfers completed
+ * after few retries.
+ */
+ if (num_completed == 0) {
+ if (stuck_req == req &&
+ readl(&ep->dma->dmadesc) !=
+ req->td_dma && stuck++ > 5) {
+ count = readl(
+ &ep->dma->dmacount);
+ count &= DMA_BYTE_COUNT_MASK;
+ req = NULL;
+ ep_dbg(ep->dev, "%s escape stuck %d, count %u\n",
+ ep->ep.name, stuck,
+ count);
+ break;
+ } else if (stuck_req != req) {
+ stuck_req = req;
+ stuck = 0;
+ }
+ } else {
+ stuck_req = NULL;
+ stuck = 0;
+ }
+
udelay(1);
}
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 9b7d394..a8709f9 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2875,7 +2875,7 @@ bad_on_1710:
xceiv = NULL;
/* "udc" is now valid */
pullup_disable(udc);
-#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+#if IS_ENABLED(CONFIG_USB_OHCI_HCD)
udc->gadget.is_otg = (config->otg != 0);
#endif
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index ad140aa..7fa60f5 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -33,6 +33,7 @@
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/phy.h>
#include "pxa27x_udc.h"
@@ -1655,6 +1656,37 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
return -EOPNOTSUPP;
}
+/**
+ * pxa_udc_phy_event - Called by phy upon VBus event
+ * @nb: notifier block
+ * @action: phy action, is vbus connect or disconnect
+ * @data: the usb_gadget structure in pxa_udc
+ *
+ * Called by the USB Phy when a cable connect or disconnect is sensed.
+ *
+ * Returns 0
+ */
+static int pxa_udc_phy_event(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct usb_gadget *gadget = data;
+
+ switch (action) {
+ case USB_EVENT_VBUS:
+ usb_gadget_vbus_connect(gadget);
+ return NOTIFY_OK;
+ case USB_EVENT_NONE:
+ usb_gadget_vbus_disconnect(gadget);
+ return NOTIFY_OK;
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static struct notifier_block pxa27x_udc_phy = {
+ .notifier_call = pxa_udc_phy_event,
+};
+
static int pxa27x_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
static int pxa27x_udc_stop(struct usb_gadget *g);
@@ -2432,7 +2464,14 @@ static int pxa_udc_probe(struct platform_device *pdev)
return udc->irq;
udc->dev = &pdev->dev;
- udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+ if (of_have_populated_dt()) {
+ udc->transceiver =
+ devm_usb_get_phy_by_phandle(udc->dev, "phys", 0);
+ if (IS_ERR(udc->transceiver))
+ return PTR_ERR(udc->transceiver);
+ } else {
+ udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+ }
if (IS_ERR(udc->gpiod)) {
dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
@@ -2465,14 +2504,20 @@ static int pxa_udc_probe(struct platform_device *pdev)
goto err;
}
+ if (!IS_ERR_OR_NULL(udc->transceiver))
+ usb_register_notifier(udc->transceiver, &pxa27x_udc_phy);
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (retval)
- goto err;
+ goto err_add_gadget;
pxa_init_debugfs(udc);
if (should_enable_udc(udc))
udc_enable(udc);
return 0;
+
+err_add_gadget:
+ if (!IS_ERR_OR_NULL(udc->transceiver))
+ usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
err:
clk_unprepare(udc->clk);
return retval;
@@ -2489,6 +2534,8 @@ static int pxa_udc_remove(struct platform_device *_dev)
usb_del_gadget_udc(&udc->gadget);
pxa_cleanup_debugfs(udc);
+ if (!IS_ERR_OR_NULL(udc->transceiver))
+ usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy);
usb_put_phy(udc->transceiver);
udc->transceiver = NULL;
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index f8bf290..588e253 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -973,10 +973,8 @@ static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep,
udc = ep->udc;
req = kzalloc(sizeof(*req), gfp_flags);
- if (!req) {
- dev_err(udc->dev, "%s:not enough memory", __func__);
+ if (!req)
return NULL;
- }
req->ep = ep;
INIT_LIST_HEAD(&req->queue);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 2e710a4..0b80cee 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -472,7 +472,7 @@ config USB_OHCI_HCD_AT91
config USB_OHCI_HCD_OMAP3
tristate "OHCI support for OMAP3 and later chips"
- depends on (ARCH_OMAP3 || ARCH_OMAP4)
+ depends on (ARCH_OMAP3 || ARCH_OMAP4 || SOC_OMAP5)
default y
---help---
Enables support for the on-chip OHCI controller on
diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c
index 172ef17..5f425c8 100644
--- a/drivers/usb/host/bcma-hcd.c
+++ b/drivers/usb/host/bcma-hcd.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
#include <linux/usb/ehci_pdriver.h>
#include <linux/usb/ohci_pdriver.h>
@@ -34,6 +35,9 @@ MODULE_AUTHOR("Hauke Mehrtens");
MODULE_DESCRIPTION("Common USB driver for BCMA Bus");
MODULE_LICENSE("GPL");
+/* See BCMA_CLKCTLST_EXTRESREQ and BCMA_CLKCTLST_EXTRESST */
+#define USB_BCMA_CLKCTLST_USB_CLK_REQ 0x00000100
+
struct bcma_hcd_device {
struct bcma_device *core;
struct platform_device *ehci_dev;
@@ -165,44 +169,80 @@ static void bcma_hcd_init_chip_mips(struct bcma_device *dev)
}
}
-static void bcma_hcd_init_chip_arm_phy(struct bcma_device *dev)
+/**
+ * bcma_hcd_usb20_old_arm_init - Initialize old USB 2.0 controller on ARM
+ *
+ * Old USB 2.0 core is identified as BCMA_CORE_USB20_HOST and was introduced
+ * long before Northstar devices. It seems some cheaper chipsets like BCM53573
+ * still use it.
+ * Initialization of this old core differs between MIPS and ARM.
+ */
+static int bcma_hcd_usb20_old_arm_init(struct bcma_hcd_device *usb_dev)
{
- struct bcma_device *arm_core;
- void __iomem *dmu;
-
- arm_core = bcma_find_core(dev->bus, BCMA_CORE_ARMCA9);
- if (!arm_core) {
- dev_err(&dev->dev, "can not find ARM Cortex A9 ihost core\n");
- return;
+ struct bcma_device *core = usb_dev->core;
+ struct device *dev = &core->dev;
+ struct bcma_device *pmu_core;
+
+ usleep_range(10000, 20000);
+ if (core->id.rev < 5)
+ return 0;
+
+ pmu_core = bcma_find_core(core->bus, BCMA_CORE_PMU);
+ if (!pmu_core) {
+ dev_err(dev, "Could not find PMU core\n");
+ return -ENOENT;
}
- dmu = ioremap_nocache(arm_core->addr_s[0], 0x1000);
- if (!dmu) {
- dev_err(&dev->dev, "can not map ARM Cortex A9 ihost core\n");
- return;
- }
-
- /* Unlock DMU PLL settings */
- iowrite32(0x0000ea68, dmu + 0x180);
-
- /* Write USB 2.0 PLL control setting */
- iowrite32(0x00dd10c3, dmu + 0x164);
+ /* Take USB core out of reset */
+ bcma_awrite32(core, BCMA_IOCTL, BCMA_IOCTL_CLK | BCMA_IOCTL_FGC);
+ usleep_range(100, 200);
+ bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
+ usleep_range(100, 200);
+ bcma_awrite32(core, BCMA_RESET_CTL, 0);
+ usleep_range(100, 200);
+ bcma_awrite32(core, BCMA_IOCTL, BCMA_IOCTL_CLK);
+ usleep_range(100, 200);
+
+ /* Enable Misc PLL */
+ bcma_write32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT |
+ BCMA_CLKCTLST_HQCLKREQ |
+ USB_BCMA_CLKCTLST_USB_CLK_REQ);
+ usleep_range(100, 200);
+
+ bcma_write32(core, 0x510, 0xc7f85000);
+ bcma_write32(core, 0x510, 0xc7f85003);
+ usleep_range(300, 600);
+
+ /* Program USB PHY PLL parameters */
+ bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_ADDR, 0x6);
+ bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_DATA, 0x005360c1);
+ usleep_range(100, 200);
+ bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_ADDR, 0x7);
+ bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_DATA, 0x0);
+ usleep_range(100, 200);
+ bcma_set32(pmu_core, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD);
+ usleep_range(100, 200);
+
+ bcma_write32(core, 0x510, 0x7f8d007);
+ udelay(1000);
+
+ /* Take controller out of reset */
+ bcma_write32(core, 0x200, 0x4ff);
+ usleep_range(25, 50);
+ bcma_write32(core, 0x200, 0x6ff);
+ usleep_range(25, 50);
+ bcma_write32(core, 0x200, 0x7ff);
+ usleep_range(25, 50);
+
+ of_platform_default_populate(dev->of_node, NULL, dev);
- /* Lock DMU PLL settings */
- iowrite32(0x00000000, dmu + 0x180);
-
- iounmap(dmu);
+ return 0;
}
-static void bcma_hcd_init_chip_arm_hc(struct bcma_device *dev)
+static void bcma_hcd_usb20_ns_init_hc(struct bcma_device *dev)
{
u32 val;
- /*
- * Delay after PHY initialized to ensure HC is ready to be configured
- */
- usleep_range(1000, 2000);
-
/* Set packet buffer OUT threshold */
val = bcma_read32(dev, 0x94);
val &= 0xffff;
@@ -213,20 +253,33 @@ static void bcma_hcd_init_chip_arm_hc(struct bcma_device *dev)
val = bcma_read32(dev, 0x9c);
val |= 1;
bcma_write32(dev, 0x9c, val);
+
+ /*
+ * Broadcom initializes PHY and then waits to ensure HC is ready to be
+ * configured. In our case the order is reversed. We just initialized
+ * controller and we let HCD initialize PHY, so let's wait (sleep) now.
+ */
+ usleep_range(1000, 2000);
}
-static void bcma_hcd_init_chip_arm(struct bcma_device *dev)
+/**
+ * bcma_hcd_usb20_ns_init - Initialize Northstar USB 2.0 controller
+ */
+static int bcma_hcd_usb20_ns_init(struct bcma_hcd_device *bcma_hcd)
{
- bcma_core_enable(dev, 0);
+ struct bcma_device *core = bcma_hcd->core;
+ struct bcma_chipinfo *ci = &core->bus->chipinfo;
+ struct device *dev = &core->dev;
- if (dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM4707 ||
- dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM53018) {
- if (dev->bus->chipinfo.pkg == BCMA_PKG_ID_BCM4707 ||
- dev->bus->chipinfo.pkg == BCMA_PKG_ID_BCM4708)
- bcma_hcd_init_chip_arm_phy(dev);
+ bcma_core_enable(core, 0);
- bcma_hcd_init_chip_arm_hc(dev);
- }
+ if (ci->id == BCMA_CHIP_ID_BCM4707 ||
+ ci->id == BCMA_CHIP_ID_BCM53018)
+ bcma_hcd_usb20_ns_init_hc(core);
+
+ of_platform_default_populate(dev->of_node, NULL, dev);
+
+ return 0;
}
static void bcma_hci_platform_power_gpio(struct bcma_device *dev, bool val)
@@ -299,16 +352,7 @@ static int bcma_hcd_usb20_init(struct bcma_hcd_device *usb_dev)
if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32)))
return -EOPNOTSUPP;
- switch (dev->id.id) {
- case BCMA_CORE_NS_USB20:
- bcma_hcd_init_chip_arm(dev);
- break;
- case BCMA_CORE_USB20_HOST:
- bcma_hcd_init_chip_mips(dev);
- break;
- default:
- return -ENODEV;
- }
+ bcma_hcd_init_chip_mips(dev);
/* In AI chips EHCI is addrspace 0, OHCI is 1 */
ohci_addr = dev->addr_s[0];
@@ -338,6 +382,18 @@ err_unregister_ohci_dev:
return err;
}
+static int bcma_hcd_usb30_init(struct bcma_hcd_device *bcma_hcd)
+{
+ struct bcma_device *core = bcma_hcd->core;
+ struct device *dev = &core->dev;
+
+ bcma_core_enable(core, 0);
+
+ of_platform_default_populate(dev->of_node, NULL, dev);
+
+ return 0;
+}
+
static int bcma_hcd_probe(struct bcma_device *core)
{
int err;
@@ -357,14 +413,24 @@ static int bcma_hcd_probe(struct bcma_device *core)
switch (core->id.id) {
case BCMA_CORE_USB20_HOST:
+ if (IS_ENABLED(CONFIG_ARM))
+ err = bcma_hcd_usb20_old_arm_init(usb_dev);
+ else if (IS_ENABLED(CONFIG_MIPS))
+ err = bcma_hcd_usb20_init(usb_dev);
+ else
+ err = -ENOTSUPP;
+ break;
case BCMA_CORE_NS_USB20:
- err = bcma_hcd_usb20_init(usb_dev);
- if (err)
- return err;
+ err = bcma_hcd_usb20_ns_init(usb_dev);
+ break;
+ case BCMA_CORE_NS_USB30:
+ err = bcma_hcd_usb30_init(usb_dev);
break;
default:
return -ENODEV;
}
+ if (err)
+ return err;
bcma_set_drvdata(core, usb_dev);
return 0;
@@ -416,6 +482,7 @@ static int bcma_hcd_resume(struct bcma_device *dev)
static const struct bcma_device_id bcma_hcd_table[] = {
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_USB20, BCMA_ANY_REV, BCMA_ANY_CLASS),
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_USB30, BCMA_ANY_REV, BCMA_ANY_CLASS),
{},
};
MODULE_DEVICE_TABLE(bcma, bcma_hcd_table);
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 6816b8c..876dca4 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -38,7 +38,7 @@
#include "ehci.h"
#define DRIVER_DESC "EHCI generic platform driver"
-#define EHCI_MAX_CLKS 3
+#define EHCI_MAX_CLKS 4
#define EHCI_MAX_RSTS 3
#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv)
diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c
index 0960f41..55a0ae6 100644
--- a/drivers/usb/host/fhci-hcd.c
+++ b/drivers/usb/host/fhci-hcd.c
@@ -310,10 +310,8 @@ static struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci)
/* allocate memory for SCC data structure */
usb = kzalloc(sizeof(*usb), GFP_KERNEL);
- if (!usb) {
- fhci_err(fhci, "no memory for SCC data struct\n");
+ if (!usb)
return NULL;
- }
usb->fhci = fhci;
usb->hc_list = fhci->hc_list;
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
index 1044b0f..f07ccb2 100644
--- a/drivers/usb/host/fsl-mph-dr-of.c
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -222,23 +222,17 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
pdata->controller_ver = usb_get_ver_info(np);
/* Activate Erratum by reading property in device tree */
- if (of_get_property(np, "fsl,usb-erratum-a007792", NULL))
- pdata->has_fsl_erratum_a007792 = 1;
- else
- pdata->has_fsl_erratum_a007792 = 0;
- if (of_get_property(np, "fsl,usb-erratum-a005275", NULL))
- pdata->has_fsl_erratum_a005275 = 1;
- else
- pdata->has_fsl_erratum_a005275 = 0;
+ pdata->has_fsl_erratum_a007792 =
+ of_property_read_bool(np, "fsl,usb-erratum-a007792");
+ pdata->has_fsl_erratum_a005275 =
+ of_property_read_bool(np, "fsl,usb-erratum-a005275");
/*
* Determine whether phy_clk_valid needs to be checked
* by reading property in device tree
*/
- if (of_get_property(np, "phy-clk-valid", NULL))
- pdata->check_phy_clk_valid = 1;
- else
- pdata->check_phy_clk_valid = 0;
+ pdata->check_phy_clk_valid =
+ of_property_read_bool(np, "phy-clk-valid");
if (pdata->have_sysif_regs) {
if (pdata->controller_ver == FSL_USB_VER_NONE) {
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 2f76900..369869a 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -1856,15 +1856,11 @@ max3421_probe(struct spi_device *spi)
INIT_LIST_HEAD(&max3421_hcd->ep_list);
max3421_hcd->tx = kmalloc(sizeof(*max3421_hcd->tx), GFP_KERNEL);
- if (!max3421_hcd->tx) {
- dev_err(&spi->dev, "failed to kmalloc tx buffer\n");
+ if (!max3421_hcd->tx)
goto error;
- }
max3421_hcd->rx = kmalloc(sizeof(*max3421_hcd->rx), GFP_KERNEL);
- if (!max3421_hcd->rx) {
- dev_err(&spi->dev, "failed to kmalloc rx buffer\n");
+ if (!max3421_hcd->rx)
goto error;
- }
max3421_hcd->spi_thread = kthread_run(max3421_spi_thread, hcd,
"max3421_spi_thread");
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index d177372..5b5880c 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -21,8 +21,11 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <soc/at91/atmel-sfr.h>
#include "ohci.h"
@@ -51,6 +54,7 @@ struct ohci_at91_priv {
struct clk *hclk;
bool clocked;
bool wakeup; /* Saved wake-up state for resume */
+ struct regmap *sfr_regmap;
};
/* interface and function clocks; sometimes also an AHB clock */
@@ -134,6 +138,17 @@ static void at91_stop_hc(struct platform_device *pdev)
static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
+static struct regmap *at91_dt_syscon_sfr(void)
+{
+ struct regmap *regmap;
+
+ regmap = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
+ if (IS_ERR(regmap))
+ regmap = NULL;
+
+ return regmap;
+}
+
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
@@ -197,6 +212,10 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
goto err;
}
+ ohci_at91->sfr_regmap = at91_dt_syscon_sfr();
+ if (!ohci_at91->sfr_regmap)
+ dev_warn(dev, "failed to find sfr node\n");
+
board = hcd->self.controller->platform_data;
ohci = hcd_to_ohci(hcd);
ohci->num_ports = board->ports;
@@ -282,6 +301,28 @@ static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)
return length;
}
+static int ohci_at91_port_suspend(struct regmap *regmap, u8 set)
+{
+ u32 regval;
+ int ret;
+
+ if (!regmap)
+ return 0;
+
+ ret = regmap_read(regmap, AT91_SFR_OHCIICR, &regval);
+ if (ret)
+ return ret;
+
+ if (set)
+ regval |= AT91_OHCIICR_USB_SUSPEND;
+ else
+ regval &= ~AT91_OHCIICR_USB_SUSPEND;
+
+ regmap_write(regmap, AT91_SFR_OHCIICR, regval);
+
+ return 0;
+}
+
/*
* Look at the control requests to the root hub and see if we need to override.
*/
@@ -289,6 +330,7 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller);
+ struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd);
struct usb_hub_descriptor *desc;
int ret = -EINVAL;
u32 *data = (u32 *)buf;
@@ -301,7 +343,8 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
switch (typeReq) {
case SetPortFeature:
- if (wValue == USB_PORT_FEAT_POWER) {
+ switch (wValue) {
+ case USB_PORT_FEAT_POWER:
dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n");
if (valid_port(wIndex)) {
ohci_at91_usb_set_power(pdata, wIndex, 1);
@@ -309,6 +352,15 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
}
goto out;
+
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(hcd->self.controller, "SetPortFeat: SUSPEND\n");
+ if (valid_port(wIndex)) {
+ ohci_at91_port_suspend(ohci_at91->sfr_regmap,
+ 1);
+ return 0;
+ }
+ break;
}
break;
@@ -342,6 +394,16 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
ohci_at91_usb_set_power(pdata, wIndex, 0);
return 0;
}
+ break;
+
+ case USB_PORT_FEAT_SUSPEND:
+ dev_dbg(hcd->self.controller, "ClearPortFeature: SUSPEND\n");
+ if (valid_port(wIndex)) {
+ ohci_at91_port_suspend(ohci_at91->sfr_regmap,
+ 0);
+ return 0;
+ }
+ break;
}
break;
}
@@ -599,6 +661,8 @@ ohci_hcd_at91_drv_suspend(struct device *dev)
if (ohci_at91->wakeup)
enable_irq_wake(hcd->irq);
+ ohci_at91_port_suspend(ohci_at91->sfr_regmap, 1);
+
ret = ohci_suspend(hcd, ohci_at91->wakeup);
if (ret) {
if (ohci_at91->wakeup)
@@ -638,6 +702,9 @@ ohci_hcd_at91_drv_resume(struct device *dev)
at91_start_clock(ohci_at91);
ohci_resume(hcd, false);
+
+ ohci_at91_port_suspend(ohci_at91->sfr_regmap, 0);
+
return 0;
}
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index de7c686..495c145 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -36,7 +36,6 @@
#include <mach/mux.h>
#include <mach/hardware.h>
-#include <mach/irqs.h>
#include <mach/usb.h>
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index 2ac266d..3a9ea32 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -13,9 +13,7 @@
* This file is licenced under the GPL.
*/
-#include <mach/hardware.h>
#include <asm/mach-types.h>
-#include <mach/assabet.h>
#include <asm/hardware/sa1111.h>
#ifndef CONFIG_SA1111
@@ -127,7 +125,7 @@ static int sa1111_start_hc(struct sa1111_dev *dev)
dev_dbg(&dev->dev, "starting SA-1111 OHCI USB Controller\n");
if (machine_is_xp860() ||
- machine_has_neponset() ||
+ machine_is_assabet() ||
machine_is_pfs168() ||
machine_is_badge4())
usb_rst = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW;
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index a7de8e8..5d3d914 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -601,11 +601,8 @@ static int uhci_start(struct usb_hcd *hcd)
uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),
GFP_KERNEL);
- if (!uhci->frame_cpu) {
- dev_err(uhci_dev(uhci),
- "unable to allocate memory for frame pointers\n");
+ if (!uhci->frame_cpu)
goto err_alloc_frame_cpu;
- }
uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),
sizeof(struct uhci_td), 16, 0);
diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c
index e363723..ad8eb57 100644
--- a/drivers/usb/host/whci/init.c
+++ b/drivers/usb/host/whci/init.c
@@ -65,7 +65,7 @@ int whc_init(struct whc *whc)
init_waitqueue_head(&whc->cmd_wq);
init_waitqueue_head(&whc->async_list_wq);
init_waitqueue_head(&whc->periodic_list_wq);
- whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev));
+ whc->workqueue = alloc_ordered_workqueue(dev_name(&whc->umc->dev), 0);
if (whc->workqueue == NULL) {
ret = -ENOMEM;
goto error;
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 0f53ae0..a59fafb 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -1033,7 +1033,6 @@ static int tegra_xusb_probe(struct platform_device *pdev)
tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
sizeof(*tegra->phys), GFP_KERNEL);
if (!tegra->phys) {
- dev_err(&pdev->dev, "failed to allocate PHY array\n");
err = -ENOMEM;
goto put_padctl;
}
@@ -1117,6 +1116,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
tegra->hcd);
if (!xhci->shared_hcd) {
dev_err(&pdev->dev, "failed to create shared HCD\n");
+ err = -ENOMEM;
goto remove_usb2;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 01d96c9..1a4ca02 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -295,10 +295,8 @@ static int xhci_setup_msix(struct xhci_hcd *xhci)
xhci->msix_entries =
kmalloc((sizeof(struct msix_entry))*xhci->msix_count,
GFP_KERNEL);
- if (!xhci->msix_entries) {
- xhci_err(xhci, "Failed to allocate MSI-X entries\n");
+ if (!xhci->msix_entries)
return -ENOMEM;
- }
for (i = 0; i < xhci->msix_count; i++) {
xhci->msix_entries[i].entry = i;
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index eb8f8d3..47b3577 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -240,6 +240,12 @@ config USB_HSIC_USB3503
help
This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver.
+config USB_HSIC_USB4604
+ tristate "USB4604 HSIC to USB20 Driver"
+ depends on I2C
+ help
+ This option enables support for SMSC USB4604 HSIC to USB 2.0 Driver.
+
config USB_LINK_LAYER_TEST
tristate "USB Link Layer Test driver"
help
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 3d79faa..3d19927 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_USB_USS720) += uss720.o
obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o
obj-$(CONFIG_USB_YUREX) += yurex.o
obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o
+obj-$(CONFIG_USB_HSIC_USB4604) += usb4604.o
obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o
obj-$(CONFIG_UCSI) += ucsi.o
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 3071c0e..564268f 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -672,8 +672,7 @@ static int adu_probe(struct usb_interface *interface,
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&interface->dev, "Out of memory\n");
+ if (!dev) {
retval = -ENOMEM;
goto exit;
}
@@ -710,7 +709,6 @@ static int adu_probe(struct usb_interface *interface,
dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL);
if (!dev->read_buffer_primary) {
- dev_err(&interface->dev, "Couldn't allocate read_buffer_primary\n");
retval = -ENOMEM;
goto error;
}
@@ -723,7 +721,6 @@ static int adu_probe(struct usb_interface *interface,
dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL);
if (!dev->read_buffer_secondary) {
- dev_err(&interface->dev, "Couldn't allocate read_buffer_secondary\n");
retval = -ENOMEM;
goto error;
}
@@ -735,29 +732,21 @@ static int adu_probe(struct usb_interface *interface,
memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size);
dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL);
- if (!dev->interrupt_in_buffer) {
- dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n");
+ if (!dev->interrupt_in_buffer)
goto error;
- }
/* debug code prime the buffer */
memset(dev->interrupt_in_buffer, 'i', in_end_size);
dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->interrupt_in_urb) {
- dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+ if (!dev->interrupt_in_urb)
goto error;
- }
dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL);
- if (!dev->interrupt_out_buffer) {
- dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n");
+ if (!dev->interrupt_out_buffer)
goto error;
- }
dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->interrupt_out_urb) {
- dev_err(&interface->dev, "Couldn't allocate interrupt_out_urb\n");
+ if (!dev->interrupt_out_urb)
goto error;
- }
if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number,
sizeof(dev->serial_number))) {
diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c
index a0a3827..da5ff40 100644
--- a/drivers/usb/misc/appledisplay.c
+++ b/drivers/usb/misc/appledisplay.c
@@ -85,7 +85,6 @@ struct appledisplay {
};
static atomic_t count_displays = ATOMIC_INIT(0);
-static struct workqueue_struct *wq;
static void appledisplay_complete(struct urb *urb)
{
@@ -122,7 +121,7 @@ static void appledisplay_complete(struct urb *urb)
case ACD_BTN_BRIGHT_UP:
case ACD_BTN_BRIGHT_DOWN:
pdata->button_pressed = 1;
- queue_delayed_work(wq, &pdata->work, 0);
+ schedule_delayed_work(&pdata->work, 0);
break;
case ACD_BTN_NONE:
default:
@@ -239,7 +238,6 @@ static int appledisplay_probe(struct usb_interface *iface,
pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);
if (!pdata) {
retval = -ENOMEM;
- dev_err(&iface->dev, "Out of memory\n");
goto error;
}
@@ -253,8 +251,6 @@ static int appledisplay_probe(struct usb_interface *iface,
pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL);
if (!pdata->msgdata) {
retval = -ENOMEM;
- dev_err(&iface->dev,
- "Allocating buffer for control messages failed\n");
goto error;
}
@@ -262,7 +258,6 @@ static int appledisplay_probe(struct usb_interface *iface,
pdata->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!pdata->urb) {
retval = -ENOMEM;
- dev_err(&iface->dev, "Allocating URB failed\n");
goto error;
}
@@ -344,7 +339,7 @@ static void appledisplay_disconnect(struct usb_interface *iface)
if (pdata) {
usb_kill_urb(pdata->urb);
- cancel_delayed_work(&pdata->work);
+ cancel_delayed_work_sync(&pdata->work);
backlight_device_unregister(pdata->bd);
usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
pdata->urbdata, pdata->urb->transfer_dma);
@@ -365,19 +360,11 @@ static struct usb_driver appledisplay_driver = {
static int __init appledisplay_init(void)
{
- wq = create_singlethread_workqueue("appledisplay");
- if (!wq) {
- printk(KERN_ERR "appledisplay: Could not create work queue\n");
- return -ENOMEM;
- }
-
return usb_register(&appledisplay_driver);
}
static void __exit appledisplay_exit(void)
{
- flush_workqueue(wq);
- destroy_workqueue(wq);
usb_deregister(&appledisplay_driver);
}
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index 402b94d..5c93a88 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -79,7 +79,6 @@ static int vendor_command(struct cypress *dev, unsigned char request,
/* allocate some memory for the i/o buffer*/
iobuf = kzalloc(CYPRESS_MAX_REQSIZE, GFP_KERNEL);
if (!iobuf) {
- dev_err(&dev->udev->dev, "Out of memory!\n");
retval = -ENOMEM;
goto error;
}
@@ -208,10 +207,8 @@ static int cypress_probe(struct usb_interface *interface,
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&interface->dev, "Out of memory!\n");
+ if (!dev)
goto error_mem;
- }
dev->udev = usb_get_dev(interface_to_usbdev(interface));
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
index 9bab1a3..9d8bb8d 100644
--- a/drivers/usb/misc/cytherm.c
+++ b/drivers/usb/misc/cytherm.c
@@ -101,10 +101,8 @@ static ssize_t set_brightness(struct device *dev, struct device_attribute *attr,
int retval;
buffer = kmalloc(8, GFP_KERNEL);
- if (!buffer) {
- dev_err(&cytherm->udev->dev, "out of memory\n");
+ if (!buffer)
return 0;
- }
cytherm->brightness = simple_strtoul(buf, NULL, 10);
@@ -148,10 +146,8 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char
int temp, sign;
buffer = kmalloc(8, GFP_KERNEL);
- if (!buffer) {
- dev_err(&cytherm->udev->dev, "out of memory\n");
+ if (!buffer)
return 0;
- }
/* read temperature */
retval = vendor_command(cytherm->udev, READ_RAM, TEMP, 0, buffer, 8);
@@ -192,10 +188,8 @@ static ssize_t show_button(struct device *dev, struct device_attribute *attr, ch
unsigned char *buffer;
buffer = kmalloc(8, GFP_KERNEL);
- if (!buffer) {
- dev_err(&cytherm->udev->dev, "out of memory\n");
+ if (!buffer)
return 0;
- }
/* check button */
retval = vendor_command(cytherm->udev, READ_RAM, BUTTON, 0, buffer, 8);
@@ -230,10 +224,8 @@ static ssize_t show_port0(struct device *dev, struct device_attribute *attr, cha
unsigned char *buffer;
buffer = kmalloc(8, GFP_KERNEL);
- if (!buffer) {
- dev_err(&cytherm->udev->dev, "out of memory\n");
+ if (!buffer)
return 0;
- }
retval = vendor_command(cytherm->udev, READ_PORT, 0, 0, buffer, 8);
if (retval)
@@ -257,10 +249,8 @@ static ssize_t set_port0(struct device *dev, struct device_attribute *attr, cons
int tmp;
buffer = kmalloc(8, GFP_KERNEL);
- if (!buffer) {
- dev_err(&cytherm->udev->dev, "out of memory\n");
+ if (!buffer)
return 0;
- }
tmp = simple_strtoul(buf, NULL, 10);
@@ -290,10 +280,8 @@ static ssize_t show_port1(struct device *dev, struct device_attribute *attr, cha
unsigned char *buffer;
buffer = kmalloc(8, GFP_KERNEL);
- if (!buffer) {
- dev_err(&cytherm->udev->dev, "out of memory\n");
+ if (!buffer)
return 0;
- }
retval = vendor_command(cytherm->udev, READ_PORT, 1, 0, buffer, 8);
if (retval)
@@ -317,10 +305,8 @@ static ssize_t set_port1(struct device *dev, struct device_attribute *attr, cons
int tmp;
buffer = kmalloc(8, GFP_KERNEL);
- if (!buffer) {
- dev_err(&cytherm->udev->dev, "out of memory\n");
+ if (!buffer)
return 0;
- }
tmp = simple_strtoul(buf, NULL, 10);
@@ -351,10 +337,8 @@ static int cytherm_probe(struct usb_interface *interface,
int retval = -ENOMEM;
dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
- if (dev == NULL) {
- dev_err (&interface->dev, "Out of memory\n");
+ if (!dev)
goto error_mem;
- }
dev->udev = usb_get_dev(udev);
diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c
index 947811b..837208f 100644
--- a/drivers/usb/misc/ezusb.c
+++ b/drivers/usb/misc/ezusb.c
@@ -22,7 +22,7 @@ struct ezusb_fx_type {
unsigned short max_internal_adress;
};
-static struct ezusb_fx_type ezusb_fx1 = {
+static const struct ezusb_fx_type ezusb_fx1 = {
.cpucs_reg = 0x7F92,
.max_internal_adress = 0x1B3F,
};
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index 9b5b3b2..9a82f83 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -61,9 +61,6 @@ module_param(distrust_firmware, bool, 0);
MODULE_PARM_DESC(distrust_firmware,
"true to distrust firmware power/overcurrent setup");
extern struct platform_driver u132_platform_driver;
-static struct workqueue_struct *status_queue;
-static struct workqueue_struct *command_queue;
-static struct workqueue_struct *respond_queue;
/*
* ftdi_module_lock exists to protect access to global variables
*
@@ -228,56 +225,56 @@ static void ftdi_elan_init_kref(struct usb_ftdi *ftdi)
static void ftdi_status_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
{
- if (!queue_delayed_work(status_queue, &ftdi->status_work, delta))
+ if (!schedule_delayed_work(&ftdi->status_work, delta))
kref_put(&ftdi->kref, ftdi_elan_delete);
}
static void ftdi_status_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
{
- if (queue_delayed_work(status_queue, &ftdi->status_work, delta))
+ if (schedule_delayed_work(&ftdi->status_work, delta))
kref_get(&ftdi->kref);
}
static void ftdi_status_cancel_work(struct usb_ftdi *ftdi)
{
- if (cancel_delayed_work(&ftdi->status_work))
+ if (cancel_delayed_work_sync(&ftdi->status_work))
kref_put(&ftdi->kref, ftdi_elan_delete);
}
static void ftdi_command_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
{
- if (!queue_delayed_work(command_queue, &ftdi->command_work, delta))
+ if (!schedule_delayed_work(&ftdi->command_work, delta))
kref_put(&ftdi->kref, ftdi_elan_delete);
}
static void ftdi_command_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
{
- if (queue_delayed_work(command_queue, &ftdi->command_work, delta))
+ if (schedule_delayed_work(&ftdi->command_work, delta))
kref_get(&ftdi->kref);
}
static void ftdi_command_cancel_work(struct usb_ftdi *ftdi)
{
- if (cancel_delayed_work(&ftdi->command_work))
+ if (cancel_delayed_work_sync(&ftdi->command_work))
kref_put(&ftdi->kref, ftdi_elan_delete);
}
static void ftdi_response_requeue_work(struct usb_ftdi *ftdi,
unsigned int delta)
{
- if (!queue_delayed_work(respond_queue, &ftdi->respond_work, delta))
+ if (!schedule_delayed_work(&ftdi->respond_work, delta))
kref_put(&ftdi->kref, ftdi_elan_delete);
}
static void ftdi_respond_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
{
- if (queue_delayed_work(respond_queue, &ftdi->respond_work, delta))
+ if (schedule_delayed_work(&ftdi->respond_work, delta))
kref_get(&ftdi->kref);
}
static void ftdi_response_cancel_work(struct usb_ftdi *ftdi)
{
- if (cancel_delayed_work(&ftdi->respond_work))
+ if (cancel_delayed_work_sync(&ftdi->respond_work))
kref_put(&ftdi->kref, ftdi_elan_delete);
}
@@ -785,11 +782,8 @@ static int ftdi_elan_command_engine(struct usb_ftdi *ftdi)
return 0;
total_size = ftdi_elan_total_command_size(ftdi, command_size);
urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- dev_err(&ftdi->udev->dev, "could not get a urb to write %d commands totaling %d bytes to the Uxxx\n",
- command_size, total_size);
+ if (!urb)
return -ENOMEM;
- }
buf = usb_alloc_coherent(ftdi->udev, total_size, GFP_KERNEL,
&urb->transfer_dma);
if (!buf) {
@@ -1948,10 +1942,8 @@ static int ftdi_elan_synchronize_flush(struct usb_ftdi *ftdi)
int I = 257;
int i = 0;
urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- dev_err(&ftdi->udev->dev, "could not alloc a urb for flush sequence\n");
+ if (!urb)
return -ENOMEM;
- }
buf = usb_alloc_coherent(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
dev_err(&ftdi->udev->dev, "could not get a buffer for flush sequence\n");
@@ -1988,10 +1980,8 @@ static int ftdi_elan_synchronize_reset(struct usb_ftdi *ftdi)
int I = 4;
int i = 0;
urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- dev_err(&ftdi->udev->dev, "could not get a urb for the reset sequence\n");
+ if (!urb)
return -ENOMEM;
- }
buf = usb_alloc_coherent(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
dev_err(&ftdi->udev->dev, "could not get a buffer for the reset sequence\n");
@@ -2740,7 +2730,6 @@ static int ftdi_elan_probe(struct usb_interface *interface,
ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress;
ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!ftdi->bulk_in_buffer) {
- dev_err(&ftdi->udev->dev, "Could not allocate bulk_in_buffer\n");
retval = -ENOMEM;
goto error;
}
@@ -2823,9 +2812,6 @@ static void ftdi_elan_disconnect(struct usb_interface *interface)
ftdi->initialized = 0;
ftdi->registered = 0;
}
- flush_workqueue(status_queue);
- flush_workqueue(command_queue);
- flush_workqueue(respond_queue);
ftdi->disconnected += 1;
usb_set_intfdata(interface, NULL);
dev_info(&ftdi->udev->dev, "USB FTDI U132 host controller interface now disconnected\n");
@@ -2845,31 +2831,12 @@ static int __init ftdi_elan_init(void)
pr_info("driver %s\n", ftdi_elan_driver.name);
mutex_init(&ftdi_module_lock);
INIT_LIST_HEAD(&ftdi_static_list);
- status_queue = create_singlethread_workqueue("ftdi-status-control");
- if (!status_queue)
- goto err_status_queue;
- command_queue = create_singlethread_workqueue("ftdi-command-engine");
- if (!command_queue)
- goto err_command_queue;
- respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
- if (!respond_queue)
- goto err_respond_queue;
result = usb_register(&ftdi_elan_driver);
if (result) {
- destroy_workqueue(status_queue);
- destroy_workqueue(command_queue);
- destroy_workqueue(respond_queue);
pr_err("usb_register failed. Error number %d\n", result);
}
return result;
-err_respond_queue:
- destroy_workqueue(command_queue);
-err_command_queue:
- destroy_workqueue(status_queue);
-err_status_queue:
- pr_err("%s couldn't create workqueue\n", ftdi_elan_driver.name);
- return -ENOMEM;
}
static void __exit ftdi_elan_exit(void)
@@ -2882,15 +2849,7 @@ static void __exit ftdi_elan_exit(void)
ftdi_status_cancel_work(ftdi);
ftdi_command_cancel_work(ftdi);
ftdi_response_cancel_work(ftdi);
- } flush_workqueue(status_queue);
- destroy_workqueue(status_queue);
- status_queue = NULL;
- flush_workqueue(command_queue);
- destroy_workqueue(command_queue);
- command_queue = NULL;
- flush_workqueue(respond_queue);
- destroy_workqueue(respond_queue);
- respond_queue = NULL;
+ }
}
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index 5105397..2975e80 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -366,7 +366,6 @@ static int idmouse_probe(struct usb_interface *interface,
kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL);
if (!dev->bulk_in_buffer) {
- dev_err(&interface->dev, "Unable to allocate input buffer.\n");
idmouse_delete(dev);
return -ENOMEM;
}
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 1950e87..095778f 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -278,7 +278,7 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer,
dev = file->private_data;
/* verify that the device wasn't unplugged */
- if (dev == NULL || !dev->present)
+ if (!dev || !dev->present)
return -ENODEV;
dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n",
@@ -413,8 +413,6 @@ static ssize_t iowarrior_write(struct file *file,
int_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!int_out_urb) {
retval = -ENOMEM;
- dev_dbg(&dev->interface->dev,
- "Unable to allocate urb\n");
goto error_no_urb;
}
buf = usb_alloc_coherent(dev->udev, dev->report_size,
@@ -482,9 +480,8 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd,
int io_res; /* checks for bytes read/written and copy_to/from_user results */
dev = file->private_data;
- if (dev == NULL) {
+ if (!dev)
return -ENODEV;
- }
buffer = kzalloc(dev->report_size, GFP_KERNEL);
if (!buffer)
@@ -654,9 +651,8 @@ static int iowarrior_release(struct inode *inode, struct file *file)
int retval = 0;
dev = file->private_data;
- if (dev == NULL) {
+ if (!dev)
return -ENODEV;
- }
dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor);
@@ -766,10 +762,8 @@ static int iowarrior_probe(struct usb_interface *interface,
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&interface->dev, "Out of memory\n");
+ if (!dev)
return retval;
- }
mutex_init(&dev->mutex);
@@ -812,15 +806,11 @@ static int iowarrior_probe(struct usb_interface *interface,
/* create the urb and buffer for reading */
dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->int_in_urb) {
- dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+ if (!dev->int_in_urb)
goto error;
- }
dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL);
- if (!dev->int_in_buffer) {
- dev_err(&interface->dev, "Couldn't allocate int_in_buffer\n");
+ if (!dev->int_in_buffer)
goto error;
- }
usb_fill_int_urb(dev->int_in_urb, dev->udev,
usb_rcvintpipe(dev->udev,
dev->int_in_endpoint->bEndpointAddress),
@@ -831,10 +821,8 @@ static int iowarrior_probe(struct usb_interface *interface,
dev->read_queue =
kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER),
GFP_KERNEL);
- if (!dev->read_queue) {
- dev_err(&interface->dev, "Couldn't allocate read_queue\n");
+ if (!dev->read_queue)
goto error;
- }
/* Get the serial-number of the chip */
memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial));
usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial,
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index cce22ff..9ca5956 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -658,10 +658,8 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&intf->dev, "Out of memory\n");
+ if (!dev)
goto exit;
- }
mutex_init(&dev->mutex);
spin_lock_init(&dev->rbsl);
dev->intf = intf;
@@ -674,10 +672,8 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
(le16_to_cpu(udev->descriptor.idProduct) == USB_DEVICE_ID_LD_COM3LAB)) &&
(le16_to_cpu(udev->descriptor.bcdDevice) <= 0x103)) {
buffer = kmalloc(256, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&intf->dev, "Couldn't allocate string buffer\n");
+ if (!buffer)
goto error;
- }
/* usb_string makes SETUP+STALL to leave always ControlReadLoop */
usb_string(udev, 255, buffer, 256);
kfree(buffer);
@@ -704,32 +700,22 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
dev->interrupt_in_endpoint_size = usb_endpoint_maxp(dev->interrupt_in_endpoint);
dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL);
- if (!dev->ring_buffer) {
- dev_err(&intf->dev, "Couldn't allocate ring_buffer\n");
+ if (!dev->ring_buffer)
goto error;
- }
dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL);
- if (!dev->interrupt_in_buffer) {
- dev_err(&intf->dev, "Couldn't allocate interrupt_in_buffer\n");
+ if (!dev->interrupt_in_buffer)
goto error;
- }
dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->interrupt_in_urb) {
- dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n");
+ if (!dev->interrupt_in_urb)
goto error;
- }
dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? usb_endpoint_maxp(dev->interrupt_out_endpoint) :
udev->descriptor.bMaxPacketSize0;
dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL);
- if (!dev->interrupt_out_buffer) {
- dev_err(&intf->dev, "Couldn't allocate interrupt_out_buffer\n");
+ if (!dev->interrupt_out_buffer)
goto error;
- }
dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->interrupt_out_urb) {
- dev_err(&intf->dev, "Couldn't allocate interrupt_out_urb\n");
+ if (!dev->interrupt_out_urb)
goto error;
- }
dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint->bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
if (dev->interrupt_out_endpoint)
dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint->bInterval ? min_interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 7771be3..c8fbe7b 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -817,10 +817,8 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(idev, "Out of memory\n");
+ if (!dev)
goto exit;
- }
mutex_init(&dev->lock);
@@ -871,51 +869,23 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
}
dev->read_buffer = kmalloc (read_buffer_size, GFP_KERNEL);
- if (!dev->read_buffer) {
- dev_err(idev, "Couldn't allocate read_buffer\n");
+ if (!dev->read_buffer)
goto error;
- }
dev->interrupt_in_buffer = kmalloc (usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL);
- if (!dev->interrupt_in_buffer) {
- dev_err(idev, "Couldn't allocate interrupt_in_buffer\n");
+ if (!dev->interrupt_in_buffer)
goto error;
- }
dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->interrupt_in_urb) {
- dev_err(idev, "Couldn't allocate interrupt_in_urb\n");
+ if (!dev->interrupt_in_urb)
goto error;
- }
dev->interrupt_out_buffer = kmalloc (write_buffer_size, GFP_KERNEL);
- if (!dev->interrupt_out_buffer) {
- dev_err(idev, "Couldn't allocate interrupt_out_buffer\n");
+ if (!dev->interrupt_out_buffer)
goto error;
- }
dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->interrupt_out_urb) {
- dev_err(idev, "Couldn't allocate interrupt_out_urb\n");
+ if (!dev->interrupt_out_urb)
goto error;
- }
dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
- /* we can register the device now, as it is ready */
- usb_set_intfdata (interface, dev);
-
- retval = usb_register_dev (interface, &tower_class);
-
- if (retval) {
- /* something prevented us from registering this driver */
- dev_err(idev, "Not able to get a minor for this device.\n");
- usb_set_intfdata (interface, NULL);
- goto error;
- }
- dev->minor = interface->minor;
-
- /* let the user know what node this device is now attached to */
- dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major "
- "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE),
- USB_MAJOR, dev->minor);
-
/* get the firmware version and log it */
result = usb_control_msg (udev,
usb_rcvctrlpipe(udev, 0),
@@ -936,6 +906,23 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
get_version_reply.minor,
le16_to_cpu(get_version_reply.build_no));
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata (interface, dev);
+
+ retval = usb_register_dev (interface, &tower_class);
+
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(idev, "Not able to get a minor for this device.\n");
+ usb_set_intfdata (interface, NULL);
+ goto error;
+ }
+ dev->minor = interface->minor;
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major "
+ "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE),
+ USB_MAJOR, dev->minor);
exit:
return retval;
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 86b4e4b..7717651 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -34,8 +34,6 @@ struct lvs_rh {
struct usb_hub_descriptor descriptor;
/* urb for polling interrupt pipe */
struct urb *urb;
- /* LVS RH work queue */
- struct workqueue_struct *rh_queue;
/* LVH RH work */
struct work_struct rh_work;
/* RH port status */
@@ -247,10 +245,8 @@ static ssize_t get_dev_desc_store(struct device *dev,
int ret;
descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL);
- if (!descriptor) {
- dev_err(dev, "failed to allocate descriptor memory\n");
+ if (!descriptor)
return -ENOMEM;
- }
udev = create_lvs_device(intf);
if (!udev) {
@@ -355,7 +351,7 @@ static void lvs_rh_irq(struct urb *urb)
{
struct lvs_rh *lvs = urb->context;
- queue_work(lvs->rh_queue, &lvs->rh_work);
+ schedule_work(&lvs->rh_work);
}
static int lvs_rh_probe(struct usb_interface *intf,
@@ -397,24 +393,15 @@ static int lvs_rh_probe(struct usb_interface *intf,
/* submit urb to poll interrupt endpoint */
lvs->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!lvs->urb) {
- dev_err(&intf->dev, "couldn't allocate lvs urb\n");
+ if (!lvs->urb)
return -ENOMEM;
- }
-
- lvs->rh_queue = create_singlethread_workqueue("lvs_rh_queue");
- if (!lvs->rh_queue) {
- dev_err(&intf->dev, "couldn't create workqueue\n");
- ret = -ENOMEM;
- goto free_urb;
- }
INIT_WORK(&lvs->rh_work, lvs_rh_work);
ret = sysfs_create_group(&intf->dev.kobj, &lvs_attr_group);
if (ret < 0) {
dev_err(&intf->dev, "Failed to create sysfs node %d\n", ret);
- goto destroy_queue;
+ goto free_urb;
}
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
@@ -432,8 +419,6 @@ static int lvs_rh_probe(struct usb_interface *intf,
sysfs_remove:
sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
-destroy_queue:
- destroy_workqueue(lvs->rh_queue);
free_urb:
usb_free_urb(lvs->urb);
return ret;
@@ -444,7 +429,7 @@ static void lvs_rh_disconnect(struct usb_interface *intf)
struct lvs_rh *lvs = usb_get_intfdata(intf);
sysfs_remove_group(&intf->dev.kobj, &lvs_attr_group);
- destroy_workqueue(lvs->rh_queue);
+ flush_work(&lvs->rh_work);
usb_free_urb(lvs->urb);
}
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 02abfcd..05bd39d 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -3084,7 +3084,6 @@ static int sisusb_probe(struct usb_interface *intf,
/* Allocate URBs */
sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL);
if (!sisusb->sisurbin) {
- dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate URBs\n");
retval = -ENOMEM;
goto error_3;
}
@@ -3093,8 +3092,6 @@ static int sisusb_probe(struct usb_interface *intf,
for (i = 0; i < sisusb->numobufs; i++) {
sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL);
if (!sisusb->sisurbout[i]) {
- dev_err(&sisusb->sisusb_dev->dev,
- "Failed to allocate URBs\n");
retval = -ENOMEM;
goto error_4;
}
diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c
index 4145314..9795457 100644
--- a/drivers/usb/misc/trancevibrator.c
+++ b/drivers/usb/misc/trancevibrator.c
@@ -95,8 +95,7 @@ static int tv_probe(struct usb_interface *interface,
int retval;
dev = kzalloc(sizeof(struct trancevibrator), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&interface->dev, "Out of memory\n");
+ if (!dev) {
retval = -ENOMEM;
goto error;
}
diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c
new file mode 100644
index 0000000..e9f37fb
--- /dev/null
+++ b/drivers/usb/misc/usb4604.c
@@ -0,0 +1,175 @@
+/*
+ * Driver for SMSC USB4604 USB HSIC 4-port 2.0 hub controller driver
+ * Based on usb3503 driver
+ *
+ * Copyright (c) 2012-2013 Dongjin Kim (tobetter@gmail.com)
+ * Copyright (c) 2016 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+
+enum usb4604_mode {
+ USB4604_MODE_UNKNOWN,
+ USB4604_MODE_HUB,
+ USB4604_MODE_STANDBY,
+};
+
+struct usb4604 {
+ enum usb4604_mode mode;
+ struct device *dev;
+ struct gpio_desc *gpio_reset;
+};
+
+static void usb4604_reset(struct usb4604 *hub, int state)
+{
+ gpiod_set_value_cansleep(hub->gpio_reset, state);
+
+ /* Wait for i2c logic to come up */
+ if (state)
+ msleep(250);
+}
+
+static int usb4604_connect(struct usb4604 *hub)
+{
+ struct device *dev = hub->dev;
+ struct i2c_client *client = to_i2c_client(dev);
+ int err;
+ u8 connect_cmd[] = { 0xaa, 0x55, 0x00 };
+
+ usb4604_reset(hub, 1);
+
+ err = i2c_master_send(client, connect_cmd, ARRAY_SIZE(connect_cmd));
+ if (err < 0) {
+ usb4604_reset(hub, 0);
+ return err;
+ }
+
+ hub->mode = USB4604_MODE_HUB;
+ dev_dbg(dev, "switched to HUB mode\n");
+
+ return 0;
+}
+
+static int usb4604_switch_mode(struct usb4604 *hub, enum usb4604_mode mode)
+{
+ struct device *dev = hub->dev;
+ int err = 0;
+
+ switch (mode) {
+ case USB4604_MODE_HUB:
+ err = usb4604_connect(hub);
+ break;
+
+ case USB4604_MODE_STANDBY:
+ usb4604_reset(hub, 0);
+ dev_dbg(dev, "switched to STANDBY mode\n");
+ break;
+
+ default:
+ dev_err(dev, "unknown mode is requested\n");
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static int usb4604_probe(struct usb4604 *hub)
+{
+ struct device *dev = hub->dev;
+ struct device_node *np = dev->of_node;
+ struct gpio_desc *gpio;
+ u32 mode = USB4604_MODE_HUB;
+
+ gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ hub->gpio_reset = gpio;
+
+ if (of_property_read_u32(np, "initial-mode", &hub->mode))
+ hub->mode = mode;
+
+ return usb4604_switch_mode(hub, hub->mode);
+}
+
+static int usb4604_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct usb4604 *hub;
+
+ hub = devm_kzalloc(&i2c->dev, sizeof(*hub), GFP_KERNEL);
+ if (!hub)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, hub);
+ hub->dev = &i2c->dev;
+
+ return usb4604_probe(hub);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int usb4604_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct usb4604 *hub = i2c_get_clientdata(client);
+
+ usb4604_switch_mode(hub, USB4604_MODE_STANDBY);
+
+ return 0;
+}
+
+static int usb4604_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct usb4604 *hub = i2c_get_clientdata(client);
+
+ usb4604_switch_mode(hub, hub->mode);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
+ usb4604_i2c_resume);
+
+static const struct i2c_device_id usb4604_id[] = {
+ { "usb4604", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, usb4604_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id usb4604_of_match[] = {
+ { .compatible = "smsc,usb4604" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, usb4604_of_match);
+#endif
+
+static struct i2c_driver usb4604_i2c_driver = {
+ .driver = {
+ .name = "usb4604",
+ .pm = &usb4604_i2c_pm_ops,
+ .of_match_table = of_match_ptr(usb4604_of_match),
+ },
+ .probe = usb4604_i2c_probe,
+ .id_table = usb4604_id,
+};
+module_i2c_driver(usb4604_i2c_driver);
+
+MODULE_DESCRIPTION("USB4604 USB HUB driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 1184390..9f48419 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -321,10 +321,8 @@ static int lcd_probe(struct usb_interface *interface,
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- dev_err(&interface->dev, "Out of memory\n");
+ if (!dev)
goto error;
- }
kref_init(&dev->kref);
sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES);
init_usb_anchor(&dev->submitted);
@@ -351,11 +349,8 @@ static int lcd_probe(struct usb_interface *interface,
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
- if (!dev->bulk_in_buffer) {
- dev_err(&interface->dev,
- "Could not allocate bulk_in_buffer\n");
+ if (!dev->bulk_in_buffer)
goto error;
- }
}
if (!dev->bulk_out_endpointAddr &&
diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c
index 1fe6b73..a0ba529 100644
--- a/drivers/usb/misc/usbsevseg.c
+++ b/drivers/usb/misc/usbsevseg.c
@@ -128,10 +128,8 @@ static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
return;
buffer = kzalloc(MAXLEN, mf);
- if (!buffer) {
- dev_err(&mydev->udev->dev, "out of memory\n");
+ if (!buffer)
return;
- }
/* The device is right to left, where as you write left to right */
for (i = 0; i < mydev->textlength; i++)
@@ -346,10 +344,8 @@ static int sevseg_probe(struct usb_interface *interface,
int rc = -ENOMEM;
mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
- if (mydev == NULL) {
- dev_err(&interface->dev, "Out of memory\n");
+ if (!mydev)
goto error_mem;
- }
mydev->udev = usb_get_dev(udev);
mydev->intf = interface;
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index bbd029c..356d312 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -150,10 +150,8 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p
if (!usbdev)
return NULL;
rq = kzalloc(sizeof(struct uss720_async_request), mem_flags);
- if (!rq) {
- dev_err(&usbdev->dev, "submit_async_request out of memory\n");
+ if (!rq)
return NULL;
- }
kref_init(&rq->ref_count);
INIT_LIST_HEAD(&rq->asynclist);
init_completion(&rq->compl);
@@ -162,7 +160,6 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p
rq->urb = usb_alloc_urb(0, mem_flags);
if (!rq->urb) {
kref_put(&rq->ref_count, destroy_async);
- dev_err(&usbdev->dev, "submit_async_request out of memory\n");
return NULL;
}
rq->dr = kmalloc(sizeof(*rq->dr), mem_flags);
diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c
index 343fa6f..54e53ac 100644
--- a/drivers/usb/misc/yurex.c
+++ b/drivers/usb/misc/yurex.c
@@ -200,10 +200,8 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- dev_err(&interface->dev, "Out of memory\n");
+ if (!dev)
goto error;
- }
kref_init(&dev->kref);
mutex_init(&dev->io_mutex);
spin_lock_init(&dev->lock);
@@ -231,17 +229,13 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_
/* allocate control URB */
dev->cntl_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->cntl_urb) {
- dev_err(&interface->dev, "Could not allocate control URB\n");
+ if (!dev->cntl_urb)
goto error;
- }
/* allocate buffer for control req */
dev->cntl_req = kmalloc(YUREX_BUF_SIZE, GFP_KERNEL);
- if (!dev->cntl_req) {
- dev_err(&interface->dev, "Could not allocate cntl_req\n");
+ if (!dev->cntl_req)
goto error;
- }
/* allocate buffer for control msg */
dev->cntl_buffer = usb_alloc_coherent(dev->udev, YUREX_BUF_SIZE,
@@ -269,10 +263,8 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_
/* allocate interrupt URB */
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->urb) {
- dev_err(&interface->dev, "Could not allocate URB\n");
+ if (!dev->urb)
goto error;
- }
/* allocate buffer for interrupt in */
dev->int_buffer = usb_alloc_coherent(dev->udev, YUREX_BUF_SIZE,
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 73cfa13..72a2a50 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -82,7 +82,7 @@ config USB_MUSB_DA8XX
tristate "DA8xx/OMAP-L1x"
depends on ARCH_DAVINCI_DA8XX
depends on NOP_USB_XCEIV
- depends on BROKEN
+ select PHY_DA8XX_USB
config USB_MUSB_TUSB6010
tristate "TUSB6010"
diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c
index c41fe58..50ca805 100644
--- a/drivers/usb/musb/am35x.c
+++ b/drivers/usb/musb/am35x.c
@@ -474,10 +474,8 @@ static int am35x_probe(struct platform_device *pdev)
int ret = -ENOMEM;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
- if (!glue) {
- dev_err(&pdev->dev, "failed to allocate glue context\n");
+ if (!glue)
goto err0;
- }
phy_clk = clk_get(&pdev->dev, "fck");
if (IS_ERR(phy_clk)) {
@@ -512,8 +510,10 @@ static int am35x_probe(struct platform_device *pdev)
pdata->platform_ops = &am35x_ops;
glue->phy = usb_phy_generic_register();
- if (IS_ERR(glue->phy))
+ if (IS_ERR(glue->phy)) {
+ ret = PTR_ERR(glue->phy);
goto err7;
+ }
platform_set_drvdata(pdev, glue);
pinfo = am35x_dev_info;
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index b03d3b8..210b7e4 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -30,13 +30,11 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/usb_phy_generic.h>
-#include <mach/da8xx.h>
-#include <linux/platform_data/usb-davinci.h>
-
#include "musb_core.h"
/*
@@ -80,61 +78,15 @@
#define DA8XX_MENTOR_CORE_OFFSET 0x400
-#define CFGCHIP2 IO_ADDRESS(DA8XX_SYSCFG0_BASE + DA8XX_CFGCHIP2_REG)
-
struct da8xx_glue {
struct device *dev;
struct platform_device *musb;
- struct platform_device *phy;
+ struct platform_device *usb_phy;
struct clk *clk;
+ struct phy *phy;
};
/*
- * REVISIT (PM): we should be able to keep the PHY in low power mode most
- * of the time (24 MHz oscillator and PLL off, etc.) by setting POWER.D0
- * and, when in host mode, autosuspending idle root ports... PHY_PLLON
- * (overriding SUSPENDM?) then likely needs to stay off.
- */
-
-static inline void phy_on(void)
-{
- u32 cfgchip2 = __raw_readl(CFGCHIP2);
-
- /*
- * Start the on-chip PHY and its PLL.
- */
- cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN);
- cfgchip2 |= CFGCHIP2_PHY_PLLON;
- __raw_writel(cfgchip2, CFGCHIP2);
-
- pr_info("Waiting for USB PHY clock good...\n");
- while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD))
- cpu_relax();
-}
-
-static inline void phy_off(void)
-{
- u32 cfgchip2 = __raw_readl(CFGCHIP2);
-
- /*
- * Ensure that USB 1.1 reference clock is not being sourced from
- * USB 2.0 PHY. Otherwise do not power down the PHY.
- */
- if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX) &&
- (cfgchip2 & CFGCHIP2_USB1SUSPENDM)) {
- pr_warning("USB 1.1 clocked from USB 2.0 PHY -- "
- "can't power it down\n");
- return;
- }
-
- /*
- * Power down the on-chip PHY.
- */
- cfgchip2 |= CFGCHIP2_PHYPWRDN | CFGCHIP2_OTGPWRDN;
- __raw_writel(cfgchip2, CFGCHIP2);
-}
-
-/*
* Because we don't set CTRL.UINT, it's "important" to:
* - not read/write INTRUSB/INTRUSBE (except during
* initial setup, as a workaround);
@@ -385,29 +337,29 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode)
{
- u32 cfgchip2 = __raw_readl(CFGCHIP2);
+ struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent);
+ enum phy_mode phy_mode;
- cfgchip2 &= ~CFGCHIP2_OTGMODE;
switch (musb_mode) {
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
- cfgchip2 |= CFGCHIP2_FORCE_HOST;
+ phy_mode = PHY_MODE_USB_HOST;
break;
case MUSB_PERIPHERAL: /* Force VBUS valid, ID = 1 */
- cfgchip2 |= CFGCHIP2_FORCE_DEVICE;
+ phy_mode = PHY_MODE_USB_DEVICE;
break;
case MUSB_OTG: /* Don't override the VBUS/ID comparators */
- cfgchip2 |= CFGCHIP2_NO_OVERRIDE;
+ phy_mode = PHY_MODE_USB_OTG;
break;
default:
- dev_dbg(musb->controller, "Trying to set unsupported mode %u\n", musb_mode);
+ return -EINVAL;
}
- __raw_writel(cfgchip2, CFGCHIP2);
- return 0;
+ return phy_set_mode(glue->phy, phy_mode);
}
static int da8xx_musb_init(struct musb *musb)
{
+ struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent);
void __iomem *reg_base = musb->ctrl_base;
u32 rev;
int ret = -ENODEV;
@@ -425,32 +377,56 @@ static int da8xx_musb_init(struct musb *musb)
goto fail;
}
+ ret = clk_prepare_enable(glue->clk);
+ if (ret) {
+ dev_err(glue->dev, "failed to enable clock\n");
+ goto fail;
+ }
+
setup_timer(&otg_workaround, otg_timer, (unsigned long)musb);
/* Reset the controller */
musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK);
/* Start the on-chip PHY and its PLL. */
- phy_on();
+ ret = phy_init(glue->phy);
+ if (ret) {
+ dev_err(glue->dev, "Failed to init phy.\n");
+ goto err_phy_init;
+ }
+
+ ret = phy_power_on(glue->phy);
+ if (ret) {
+ dev_err(glue->dev, "Failed to power on phy.\n");
+ goto err_phy_power_on;
+ }
msleep(5);
/* NOTE: IRQs are in mixed mode, not bypass to pure MUSB */
- pr_debug("DA8xx OTG revision %08x, PHY %03x, control %02x\n",
- rev, __raw_readl(CFGCHIP2),
+ pr_debug("DA8xx OTG revision %08x, control %02x\n", rev,
musb_readb(reg_base, DA8XX_USB_CTRL_REG));
musb->isr = da8xx_musb_interrupt;
return 0;
+
+err_phy_power_on:
+ phy_exit(glue->phy);
+err_phy_init:
+ clk_disable_unprepare(glue->clk);
fail:
return ret;
}
static int da8xx_musb_exit(struct musb *musb)
{
+ struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent);
+
del_timer_sync(&otg_workaround);
- phy_off();
+ phy_power_off(glue->phy);
+ phy_exit(glue->phy);
+ clk_disable_unprepare(glue->clk);
usb_put_phy(musb->xceiv);
@@ -486,30 +462,25 @@ static int da8xx_probe(struct platform_device *pdev)
{
struct resource musb_resources[2];
struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct platform_device *musb;
struct da8xx_glue *glue;
struct platform_device_info pinfo;
struct clk *clk;
+ int ret;
- int ret = -ENOMEM;
-
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
- if (!glue) {
- dev_err(&pdev->dev, "failed to allocate glue context\n");
- goto err0;
- }
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ return -ENOMEM;
- clk = clk_get(&pdev->dev, "usb20");
+ clk = devm_clk_get(&pdev->dev, "usb20");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
- ret = PTR_ERR(clk);
- goto err3;
+ return PTR_ERR(clk);
}
- ret = clk_enable(clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable clock\n");
- goto err4;
+ glue->phy = devm_phy_get(&pdev->dev, "usb-phy");
+ if (IS_ERR(glue->phy)) {
+ dev_err(&pdev->dev, "failed to get phy\n");
+ return PTR_ERR(glue->phy);
}
glue->dev = &pdev->dev;
@@ -517,10 +488,11 @@ static int da8xx_probe(struct platform_device *pdev)
pdata->platform_ops = &da8xx_ops;
- glue->phy = usb_phy_generic_register();
- if (IS_ERR(glue->phy)) {
- ret = PTR_ERR(glue->phy);
- goto err5;
+ glue->usb_phy = usb_phy_generic_register();
+ ret = PTR_ERR_OR_ZERO(glue->usb_phy);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register usb_phy\n");
+ return ret;
}
platform_set_drvdata(pdev, glue);
@@ -544,28 +516,13 @@ static int da8xx_probe(struct platform_device *pdev)
pinfo.data = pdata;
pinfo.size_data = sizeof(*pdata);
- glue->musb = musb = platform_device_register_full(&pinfo);
- if (IS_ERR(musb)) {
- ret = PTR_ERR(musb);
+ glue->musb = platform_device_register_full(&pinfo);
+ ret = PTR_ERR_OR_ZERO(glue->musb);
+ if (ret) {
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
- goto err6;
+ usb_phy_generic_unregister(glue->usb_phy);
}
- return 0;
-
-err6:
- usb_phy_generic_unregister(glue->phy);
-
-err5:
- clk_disable(clk);
-
-err4:
- clk_put(clk);
-
-err3:
- kfree(glue);
-
-err0:
return ret;
}
@@ -574,10 +531,7 @@ static int da8xx_remove(struct platform_device *pdev)
struct da8xx_glue *glue = platform_get_drvdata(pdev);
platform_device_unregister(glue->musb);
- usb_phy_generic_unregister(glue->phy);
- clk_disable(glue->clk);
- clk_put(glue->clk);
- kfree(glue);
+ usb_phy_generic_unregister(glue->usb_phy);
return 0;
}
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 74fc306..27dadc0 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -1448,7 +1448,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
{
u8 reg;
char *type;
- char aInfo[90], aRevision[32], aDate[12];
+ char aInfo[90];
void __iomem *mbase = musb->mregs;
int status = 0;
int i;
@@ -1482,7 +1482,6 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
pr_debug("%s: ConfigData=0x%02x (%s)\n", musb_driver_name, reg, aInfo);
- aDate[0] = 0;
if (MUSB_CONTROLLER_MHDRC == musb_type) {
musb->is_multipoint = 1;
type = "M";
@@ -1497,11 +1496,10 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
/* log release info */
musb->hwvers = musb_read_hwvers(mbase);
- snprintf(aRevision, 32, "%d.%d%s", MUSB_HWVERS_MAJOR(musb->hwvers),
- MUSB_HWVERS_MINOR(musb->hwvers),
- (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : "");
- pr_debug("%s: %sHDRC RTL version %s %s\n",
- musb_driver_name, type, aRevision, aDate);
+ pr_debug("%s: %sHDRC RTL version %d.%d%s\n",
+ musb_driver_name, type, MUSB_HWVERS_MAJOR(musb->hwvers),
+ MUSB_HWVERS_MINOR(musb->hwvers),
+ (musb->hwvers & MUSB_HWVERS_RC) ? "RC" : "");
/* configure ep0 */
musb_configure_ep0(musb);
@@ -1831,11 +1829,80 @@ static const struct attribute_group musb_attr_group = {
.attrs = musb_attributes,
};
+#define MUSB_QUIRK_B_INVALID_VBUS_91 (MUSB_DEVCTL_BDEVICE | \
+ (2 << MUSB_DEVCTL_VBUS_SHIFT) | \
+ MUSB_DEVCTL_SESSION)
+#define MUSB_QUIRK_A_DISCONNECT_19 ((3 << MUSB_DEVCTL_VBUS_SHIFT) | \
+ MUSB_DEVCTL_SESSION)
+
+/*
+ * Check the musb devctl session bit to determine if we want to
+ * allow PM runtime for the device. In general, we want to keep things
+ * active when the session bit is set except after host disconnect.
+ *
+ * Only called from musb_irq_work. If this ever needs to get called
+ * elsewhere, proper locking must be implemented for musb->session.
+ */
+static void musb_pm_runtime_check_session(struct musb *musb)
+{
+ u8 devctl, s;
+ int error;
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ /* Handle session status quirks first */
+ s = MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV |
+ MUSB_DEVCTL_HR;
+ switch (devctl & ~s) {
+ case MUSB_QUIRK_B_INVALID_VBUS_91:
+ if (!musb->session && !musb->quirk_invalid_vbus) {
+ musb->quirk_invalid_vbus = true;
+ musb_dbg(musb,
+ "First invalid vbus, assume no session");
+ return;
+ }
+ break;
+ case MUSB_QUIRK_A_DISCONNECT_19:
+ if (!musb->session)
+ break;
+ musb_dbg(musb, "Allow PM on possible host mode disconnect");
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
+ musb->session = false;
+ return;
+ default:
+ break;
+ }
+
+ /* No need to do anything if session has not changed */
+ s = devctl & MUSB_DEVCTL_SESSION;
+ if (s == musb->session)
+ return;
+
+ /* Block PM or allow PM? */
+ if (s) {
+ musb_dbg(musb, "Block PM on active session: %02x", devctl);
+ error = pm_runtime_get_sync(musb->controller);
+ if (error < 0)
+ dev_err(musb->controller, "Could not enable: %i\n",
+ error);
+ } else {
+ musb_dbg(musb, "Allow PM with no session: %02x", devctl);
+ musb->quirk_invalid_vbus = false;
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
+ }
+
+ musb->session = s;
+}
+
/* Only used to provide driver mode change events */
static void musb_irq_work(struct work_struct *data)
{
struct musb *musb = container_of(data, struct musb, irq_work);
+ musb_pm_runtime_check_session(musb);
+
if (musb->xceiv->otg->state != musb->xceiv_old_state) {
musb->xceiv_old_state = musb->xceiv->otg->state;
sysfs_notify(&musb->controller->kobj, NULL, "mode");
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index b55a776..2cb88a49 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -378,6 +378,8 @@ struct musb {
u8 min_power; /* vbus for periph, in mA/2 */
int port_mode; /* MUSB_PORT_MODE_* */
+ bool session;
+ bool quirk_invalid_vbus;
bool is_host;
int a_wait_bcon; /* VBUS timeout in msecs */
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 2537179..0f17d21 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -145,43 +145,6 @@ static const struct debugfs_reg32 dsps_musb_regs[] = {
{ "mode", 0xe8 },
};
-static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
-{
- struct device *dev = musb->controller;
- struct dsps_glue *glue = dev_get_drvdata(dev->parent);
-
- if (timeout == 0)
- timeout = jiffies + msecs_to_jiffies(3);
-
- /* Never idle if active, or when VBUS timeout is not set as host */
- if (musb->is_active || (musb->a_wait_bcon == 0 &&
- musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
- dev_dbg(musb->controller, "%s active, deleting timer\n",
- usb_otg_state_string(musb->xceiv->otg->state));
- del_timer(&glue->timer);
- glue->last_timer = jiffies;
- return;
- }
- if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE)
- return;
-
- if (!musb->g.dev.driver)
- return;
-
- if (time_after(glue->last_timer, timeout) &&
- timer_pending(&glue->timer)) {
- dev_dbg(musb->controller,
- "Longer idle timer already pending, ignoring...\n");
- return;
- }
- glue->last_timer = timeout;
-
- dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
- usb_otg_state_string(musb->xceiv->otg->state),
- jiffies_to_msecs(timeout - jiffies));
- mod_timer(&glue->timer, timeout);
-}
-
/**
* dsps_musb_enable - enable interrupts
*/
@@ -206,7 +169,6 @@ static void dsps_musb_enable(struct musb *musb)
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
mod_timer(&glue->timer, jiffies +
msecs_to_jiffies(wrp->poll_timeout));
- dsps_musb_try_idle(musb, 0);
}
/**
@@ -236,6 +198,11 @@ static void otg_timer(unsigned long _musb)
u8 devctl;
unsigned long flags;
int skip_session = 0;
+ int err;
+
+ err = pm_runtime_get_sync(dev);
+ if (err < 0)
+ dev_err(dev, "Poll could not pm_runtime_get: %i\n", err);
/*
* We poll because DSPS IP's won't expose several OTG-critical
@@ -247,6 +214,10 @@ static void otg_timer(unsigned long _musb)
spin_lock_irqsave(&musb->lock, flags);
switch (musb->xceiv->otg->state) {
+ case OTG_STATE_A_WAIT_VRISE:
+ mod_timer(&glue->timer, jiffies +
+ msecs_to_jiffies(wrp->poll_timeout));
+ break;
case OTG_STATE_A_WAIT_BCON:
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
skip_session = 1;
@@ -275,6 +246,9 @@ static void otg_timer(unsigned long _musb)
break;
}
spin_unlock_irqrestore(&musb->lock, flags);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
}
static irqreturn_t dsps_interrupt(int irq, void *hci)
@@ -338,7 +312,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
MUSB_HST_MODE(musb);
musb->xceiv->otg->default_a = 1;
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
- del_timer(&glue->timer);
+ mod_timer(&glue->timer, jiffies +
+ msecs_to_jiffies(wrp->poll_timeout));
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
@@ -358,11 +333,17 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
if (musb->int_tx || musb->int_rx || musb->int_usb)
ret |= musb_interrupt(musb);
- /* Poll for ID change in OTG port mode */
- if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
- musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
+ /* Poll for ID change and connect */
+ switch (musb->xceiv->otg->state) {
+ case OTG_STATE_B_IDLE:
+ case OTG_STATE_A_WAIT_BCON:
mod_timer(&glue->timer, jiffies +
msecs_to_jiffies(wrp->poll_timeout));
+ break;
+ default:
+ break;
+ }
+
out:
spin_unlock_irqrestore(&musb->lock, flags);
@@ -461,6 +442,9 @@ static int dsps_musb_init(struct musb *musb)
musb_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
}
+ mod_timer(&glue->timer, jiffies +
+ msecs_to_jiffies(glue->wrp->poll_timeout));
+
return dsps_musb_dbg_init(musb, glue);
}
@@ -620,7 +604,6 @@ static struct musb_platform_ops dsps_ops = {
.enable = dsps_musb_enable,
.disable = dsps_musb_disable,
- .try_idle = dsps_musb_try_idle,
.set_mode = dsps_musb_set_mode,
.recover = dsps_musb_recover,
};
@@ -784,6 +767,8 @@ static int dsps_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, glue);
pm_runtime_enable(&pdev->dev);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 200);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
@@ -795,11 +780,15 @@ static int dsps_probe(struct platform_device *pdev)
if (ret)
goto err3;
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+
return 0;
err3:
- pm_runtime_put(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
err2:
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -811,7 +800,8 @@ static int dsps_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
/* disable usbss clocks */
- pm_runtime_put(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 6d1e975..bff4869 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1964,6 +1964,9 @@ static int musb_gadget_stop(struct usb_gadget *g)
* that currently misbehaves.
*/
+ /* Force check of devctl register for PM runtime */
+ schedule_work(&musb->irq_work);
+
pm_runtime_mark_last_busy(musb->controller);
pm_runtime_put_autosuspend(musb->controller);
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index fe08e77..61b5f1c 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -245,6 +245,7 @@ void musb_root_disconnect(struct musb *musb)
usb_otg_state_string(musb->xceiv->otg->state));
}
}
+EXPORT_SYMBOL_GPL(musb_root_disconnect);
/*---------------------------------------------------------------------*/
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 0b4cec9..1ab6973 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -49,9 +49,6 @@ struct omap2430_glue {
enum musb_vbus_id_status status;
struct work_struct omap_musb_mailbox_work;
struct device *control_otghs;
- bool cable_connected;
- bool enabled;
- bool powered;
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
@@ -141,45 +138,6 @@ static inline void omap2430_low_level_init(struct musb *musb)
musb_writel(musb->mregs, OTG_FORCESTDBY, l);
}
-/*
- * We can get multiple cable events so we need to keep track
- * of the power state. Only keep power enabled if USB cable is
- * connected and a gadget is started.
- */
-static void omap2430_set_power(struct musb *musb, bool enabled, bool cable)
-{
- struct device *dev = musb->controller;
- struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
- bool power_up;
- int res;
-
- if (glue->enabled != enabled)
- glue->enabled = enabled;
-
- if (glue->cable_connected != cable)
- glue->cable_connected = cable;
-
- power_up = glue->enabled && glue->cable_connected;
- if (power_up == glue->powered) {
- dev_warn(musb->controller, "power state already %i\n",
- power_up);
- return;
- }
-
- glue->powered = power_up;
-
- if (power_up) {
- res = pm_runtime_get_sync(musb->controller);
- if (res < 0) {
- dev_err(musb->controller, "could not enable: %i", res);
- glue->powered = false;
- }
- } else {
- pm_runtime_mark_last_busy(musb->controller);
- pm_runtime_put_autosuspend(musb->controller);
- }
-}
-
static int omap2430_musb_mailbox(enum musb_vbus_id_status status)
{
struct omap2430_glue *glue = _glue;
@@ -203,21 +161,15 @@ static int omap2430_musb_mailbox(enum musb_vbus_id_status status)
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
{
struct musb *musb = glue_to_musb(glue);
- struct device *dev = musb->controller;
- struct musb_hdrc_platform_data *pdata = dev_get_platdata(dev);
+ struct musb_hdrc_platform_data *pdata =
+ dev_get_platdata(musb->controller);
struct omap_musb_board_data *data = pdata->board_data;
struct usb_otg *otg = musb->xceiv->otg;
- bool cable_connected;
-
- cable_connected = ((glue->status == MUSB_ID_GROUND) ||
- (glue->status == MUSB_VBUS_VALID));
-
- if (cable_connected)
- omap2430_set_power(musb, glue->enabled, cable_connected);
+ pm_runtime_get_sync(musb->controller);
switch (glue->status) {
case MUSB_ID_GROUND:
- dev_dbg(dev, "ID GND\n");
+ dev_dbg(musb->controller, "ID GND\n");
otg->default_a = true;
musb->xceiv->otg->state = OTG_STATE_A_IDLE;
@@ -230,7 +182,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
break;
case MUSB_VBUS_VALID:
- dev_dbg(dev, "VBUS Connect\n");
+ dev_dbg(musb->controller, "VBUS Connect\n");
otg->default_a = false;
musb->xceiv->otg->state = OTG_STATE_B_IDLE;
@@ -240,7 +192,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
case MUSB_ID_FLOAT:
case MUSB_VBUS_OFF:
- dev_dbg(dev, "VBUS Disconnect\n");
+ dev_dbg(musb->controller, "VBUS Disconnect\n");
musb->xceiv->last_event = USB_EVENT_NONE;
if (musb->gadget_driver)
@@ -253,12 +205,10 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
USB_MODE_DISCONNECT);
break;
default:
- dev_dbg(dev, "ID float\n");
+ dev_dbg(musb->controller, "ID float\n");
}
-
- if (!cable_connected)
- omap2430_set_power(musb, glue->enabled, cable_connected);
-
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
atomic_notifier_call_chain(&musb->xceiv->notifier,
musb->xceiv->last_event, NULL);
}
@@ -376,8 +326,6 @@ static void omap2430_musb_enable(struct musb *musb)
if (!WARN_ON(!musb->phy))
phy_power_on(musb->phy);
- omap2430_set_power(musb, true, glue->cable_connected);
-
switch (glue->status) {
case MUSB_ID_GROUND:
@@ -419,8 +367,6 @@ static void omap2430_musb_disable(struct musb *musb)
if (glue->status != MUSB_UNKNOWN)
omap_control_usb_set_mode(glue->control_otghs,
USB_MODE_DISCONNECT);
-
- omap2430_set_power(musb, false, glue->cable_connected);
}
static int omap2430_musb_exit(struct musb *musb)
@@ -571,7 +517,7 @@ static int omap2430_probe(struct platform_device *pdev)
pm_runtime_enable(glue->dev);
pm_runtime_use_autosuspend(glue->dev);
- pm_runtime_set_autosuspend_delay(glue->dev, 500);
+ pm_runtime_set_autosuspend_delay(glue->dev, 100);
ret = platform_device_add(musb);
if (ret) {
@@ -591,11 +537,9 @@ err0:
static int omap2430_remove(struct platform_device *pdev)
{
struct omap2430_glue *glue = platform_get_drvdata(pdev);
- struct musb *musb = glue_to_musb(glue);
pm_runtime_get_sync(glue->dev);
platform_device_unregister(glue->musb);
- omap2430_set_power(musb, false, false);
pm_runtime_put_sync(glue->dev);
pm_runtime_dont_use_autosuspend(glue->dev);
pm_runtime_disable(glue->dev);
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index c6ee166..1408245 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -74,6 +74,7 @@
#define SUNXI_MUSB_FL_HAS_SRAM 5
#define SUNXI_MUSB_FL_HAS_RESET 6
#define SUNXI_MUSB_FL_NO_CONFIGDATA 7
+#define SUNXI_MUSB_FL_PHY_MODE_PEND 8
/* Our read/write methods need access and do not get passed in a musb ref :| */
static struct musb *sunxi_musb;
@@ -87,6 +88,7 @@ struct sunxi_glue {
struct phy *phy;
struct platform_device *usb_phy;
struct usb_phy *xceiv;
+ enum phy_mode phy_mode;
unsigned long flags;
struct work_struct work;
struct extcon_dev *extcon;
@@ -140,6 +142,9 @@ static void sunxi_musb_work(struct work_struct *work)
clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags);
}
}
+
+ if (test_and_clear_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags))
+ phy_set_mode(glue->phy, glue->phy_mode);
}
static void sunxi_musb_set_vbus(struct musb *musb, int is_on)
@@ -341,6 +346,50 @@ static void sunxi_musb_dma_controller_destroy(struct dma_controller *c)
{
}
+static int sunxi_musb_set_mode(struct musb *musb, u8 mode)
+{
+ struct sunxi_glue *glue = dev_get_drvdata(musb->controller->parent);
+ enum phy_mode new_mode;
+
+ switch (mode) {
+ case MUSB_HOST:
+ new_mode = PHY_MODE_USB_HOST;
+ break;
+ case MUSB_PERIPHERAL:
+ new_mode = PHY_MODE_USB_DEVICE;
+ break;
+ case MUSB_OTG:
+ new_mode = PHY_MODE_USB_OTG;
+ break;
+ default:
+ dev_err(musb->controller->parent,
+ "Error requested mode not supported by this kernel\n");
+ return -EINVAL;
+ }
+
+ if (glue->phy_mode == new_mode)
+ return 0;
+
+ if (musb->port_mode != MUSB_PORT_MODE_DUAL_ROLE) {
+ dev_err(musb->controller->parent,
+ "Error changing modes is only supported in dual role mode\n");
+ return -EINVAL;
+ }
+
+ if (musb->port1_status & USB_PORT_STAT_ENABLE)
+ musb_root_disconnect(musb);
+
+ /*
+ * phy_set_mode may sleep, and we're called with a spinlock held,
+ * so let sunxi_musb_work deal with it.
+ */
+ glue->phy_mode = new_mode;
+ set_bit(SUNXI_MUSB_FL_PHY_MODE_PEND, &glue->flags);
+ schedule_work(&glue->work);
+
+ return 0;
+}
+
/*
* sunxi musb register layout
* 0x00 - 0x17 fifo regs, 1 long per fifo
@@ -568,6 +617,7 @@ static const struct musb_platform_ops sunxi_musb_ops = {
.writew = sunxi_musb_writew,
.dma_init = sunxi_musb_dma_controller_create,
.dma_exit = sunxi_musb_dma_controller_destroy,
+ .set_mode = sunxi_musb_set_mode,
.set_vbus = sunxi_musb_set_vbus,
.pre_root_reset_end = sunxi_musb_pre_root_reset_end,
.post_root_reset_end = sunxi_musb_post_root_reset_end,
@@ -614,21 +664,28 @@ static int sunxi_musb_probe(struct platform_device *pdev)
return -EINVAL;
}
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ return -ENOMEM;
+
memset(&pdata, 0, sizeof(pdata));
switch (usb_get_dr_mode(&pdev->dev)) {
#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST
case USB_DR_MODE_HOST:
pdata.mode = MUSB_PORT_MODE_HOST;
+ glue->phy_mode = PHY_MODE_USB_HOST;
break;
#endif
#if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_GADGET
case USB_DR_MODE_PERIPHERAL:
pdata.mode = MUSB_PORT_MODE_GADGET;
+ glue->phy_mode = PHY_MODE_USB_DEVICE;
break;
#endif
#ifdef CONFIG_USB_MUSB_DUAL_ROLE
case USB_DR_MODE_OTG:
pdata.mode = MUSB_PORT_MODE_DUAL_ROLE;
+ glue->phy_mode = PHY_MODE_USB_OTG;
break;
#endif
default:
@@ -638,10 +695,6 @@ static int sunxi_musb_probe(struct platform_device *pdev)
pdata.platform_ops = &sunxi_musb_ops;
pdata.config = &sunxi_musb_hdrc_config;
- glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
- if (!glue)
- return -ENOMEM;
-
glue->dev = &pdev->dev;
INIT_WORK(&glue->work, sunxi_musb_work);
glue->host_nb.notifier_call = sunxi_musb_host_notifier;
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 0c912d3..a03caf4 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -1248,7 +1248,7 @@ static void ab8500_usb_set_ab8500_tuning_values(struct ab8500_usb *ab)
err = abx500_set_register_interruptible(ab->dev,
AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x78);
if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n",
+ dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
err);
/* Switch to normal mode/disable Bank 0x12 access */
@@ -1290,7 +1290,7 @@ static void ab8500_usb_set_ab8505_tuning_values(struct ab8500_usb *ab)
0xFC, 0x80);
if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n",
+ dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
err);
/* Switch to normal mode/disable Bank 0x12 access */
@@ -1321,7 +1321,7 @@ static void ab8500_usb_set_ab8540_tuning_values(struct ab8500_usb *ab)
err = abx500_set_register_interruptible(ab->dev,
AB8540_DEBUG, AB8500_USB_PHY_TUNE3, 0x90);
if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE3 regester ret=%d\n",
+ dev_err(ab->dev, "Failed to set PHY_TUNE3 register ret=%d\n",
err);
}
@@ -1351,7 +1351,7 @@ static void ab8500_usb_set_ab9540_tuning_values(struct ab8500_usb *ab)
err = abx500_set_register_interruptible(ab->dev,
AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x80);
if (err < 0)
- dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n",
+ dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
err);
/* Switch to normal mode/disable Bank 0x12 access */
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 427efb5..8311ba2 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -118,8 +118,6 @@ static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
status = USB_EVENT_VBUS;
otg->state = OTG_STATE_B_PERIPHERAL;
nop->phy.last_event = status;
- if (otg->gadget)
- usb_gadget_vbus_connect(otg->gadget);
/* drawing a "unit load" is *always* OK, except for OTG */
nop_set_vbus_draw(nop, 100);
@@ -129,8 +127,6 @@ static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
} else {
nop_set_vbus_draw(nop, 0);
- if (otg->gadget)
- usb_gadget_vbus_disconnect(otg->gadget);
status = USB_EVENT_NONE;
otg->state = OTG_STATE_B_IDLE;
nop->phy.last_event = status;
@@ -191,7 +187,8 @@ static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
otg->gadget = gadget;
if (otg->state == OTG_STATE_B_PERIPHERAL)
- usb_gadget_vbus_connect(gadget);
+ atomic_notifier_call_chain(&otg->usb_phy->notifier,
+ USB_EVENT_VBUS, otg->gadget);
else
otg->state = OTG_STATE_B_IDLE;
return 0;
@@ -326,6 +323,8 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
gpiod_to_irq(nop->gpiod_vbus), err);
return err;
}
+ nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
+ OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE;
}
nop->phy.init = usb_gen_phy_init;
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 00bfea0..0e2f1a3 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -27,6 +27,7 @@
#define DRIVER_NAME "mxs_phy"
#define HW_USBPHY_PWD 0x00
+#define HW_USBPHY_TX 0x10
#define HW_USBPHY_CTRL 0x30
#define HW_USBPHY_CTRL_SET 0x34
#define HW_USBPHY_CTRL_CLR 0x38
@@ -38,6 +39,10 @@
#define HW_USBPHY_IP_SET 0x94
#define HW_USBPHY_IP_CLR 0x98
+#define GM_USBPHY_TX_TXCAL45DP(x) (((x) & 0xf) << 16)
+#define GM_USBPHY_TX_TXCAL45DN(x) (((x) & 0xf) << 8)
+#define GM_USBPHY_TX_D_CAL(x) (((x) & 0xf) << 0)
+
#define BM_USBPHY_CTRL_SFTRST BIT(31)
#define BM_USBPHY_CTRL_CLKGATE BIT(30)
#define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27)
@@ -115,6 +120,12 @@
*/
#define MXS_PHY_NEED_IP_FIX BIT(3)
+/* Minimum and maximum values for device tree entries */
+#define MXS_PHY_TX_CAL45_MIN 30
+#define MXS_PHY_TX_CAL45_MAX 55
+#define MXS_PHY_TX_D_CAL_MIN 79
+#define MXS_PHY_TX_D_CAL_MAX 119
+
struct mxs_phy_data {
unsigned int flags;
};
@@ -164,6 +175,8 @@ struct mxs_phy {
const struct mxs_phy_data *data;
struct regmap *regmap_anatop;
int port_id;
+ u32 tx_reg_set;
+ u32 tx_reg_mask;
};
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
@@ -185,6 +198,20 @@ static void mxs_phy_clock_switch_delay(void)
usleep_range(300, 400);
}
+static void mxs_phy_tx_init(struct mxs_phy *mxs_phy)
+{
+ void __iomem *base = mxs_phy->phy.io_priv;
+ u32 phytx;
+
+ /* Update TX register if there is anything to write */
+ if (mxs_phy->tx_reg_mask) {
+ phytx = readl(base + HW_USBPHY_TX);
+ phytx &= ~mxs_phy->tx_reg_mask;
+ phytx |= mxs_phy->tx_reg_set;
+ writel(phytx, base + HW_USBPHY_TX);
+ }
+}
+
static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
{
int ret;
@@ -214,6 +241,8 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX)
writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET);
+ mxs_phy_tx_init(mxs_phy);
+
return 0;
}
@@ -459,6 +488,7 @@ static int mxs_phy_probe(struct platform_device *pdev)
int ret;
const struct of_device_id *of_id;
struct device_node *np = pdev->dev.of_node;
+ u32 val;
of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev);
if (!of_id)
@@ -491,6 +521,37 @@ static int mxs_phy_probe(struct platform_device *pdev)
}
}
+ /* Precompute which bits of the TX register are to be updated, if any */
+ if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
+ val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
+ /* Scale to a 4-bit value */
+ val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF
+ / (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN);
+ mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DN(~0);
+ mxs_phy->tx_reg_set |= GM_USBPHY_TX_TXCAL45DN(val);
+ }
+
+ if (!of_property_read_u32(np, "fsl,tx-cal-45-dp-ohms", &val) &&
+ val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
+ /* Scale to a 4-bit value. */
+ val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF
+ / (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN);
+ mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DP(~0);
+ mxs_phy->tx_reg_set |= GM_USBPHY_TX_TXCAL45DP(val);
+ }
+
+ if (!of_property_read_u32(np, "fsl,tx-d-cal", &val) &&
+ val >= MXS_PHY_TX_D_CAL_MIN && val <= MXS_PHY_TX_D_CAL_MAX) {
+ /* Scale to a 4-bit value. Round up the values and heavily
+ * weight the rounding by adding 2/3 of the denominator.
+ */
+ val = ((MXS_PHY_TX_D_CAL_MAX - val) * 0xF
+ + (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN) * 2/3)
+ / (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN);
+ mxs_phy->tx_reg_mask |= GM_USBPHY_TX_D_CAL(~0);
+ mxs_phy->tx_reg_set |= GM_USBPHY_TX_D_CAL(val);
+ }
+
ret = of_alias_get_id(np, "usbphy");
if (ret < 0)
dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index ac67bab..012a37a 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -482,6 +482,10 @@ static const struct of_device_id usbhs_of_match[] = {
.data = (void *)USBHS_TYPE_RCAR_GEN3,
},
{
+ .compatible = "renesas,usbhs-r8a7796",
+ .data = (void *)USBHS_TYPE_RCAR_GEN3,
+ },
+ {
.compatible = "renesas,rcar-gen2-usbhs",
.data = (void *)USBHS_TYPE_RCAR_GEN2,
},
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index c4c6474..5bc7a61 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -335,7 +335,6 @@ static void __usbhsg_recip_send_status(struct usbhsg_gpriv *gpriv,
buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
if (!buf) {
usb_ep_free_request(&dcp->ep, req);
- dev_err(dev, "recip data allocation fail\n");
return;
}
@@ -1062,14 +1061,11 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
int ret;
gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL);
- if (!gpriv) {
- dev_err(dev, "Could not allocate gadget priv\n");
+ if (!gpriv)
return -ENOMEM;
- }
uep = kzalloc(sizeof(struct usbhsg_uep) * pipe_size, GFP_KERNEL);
if (!uep) {
- dev_err(dev, "Could not allocate ep\n");
ret = -ENOMEM;
goto usbhs_mod_gadget_probe_err_gpriv;
}
@@ -1106,6 +1102,8 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
gpriv->gadget.name = "renesas_usbhs_udc";
gpriv->gadget.ops = &usbhsg_gadget_ops;
gpriv->gadget.max_speed = USB_SPEED_HIGH;
+ gpriv->gadget.quirk_avoids_skb_reserve = usbhs_get_dparam(priv,
+ has_usb_dmac);
INIT_LIST_HEAD(&gpriv->gadget.ep_list);
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index 3bf0b72..165e81b 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -166,14 +166,10 @@ static struct usbhsh_request *usbhsh_ureq_alloc(struct usbhsh_hpriv *hpriv,
gfp_t mem_flags)
{
struct usbhsh_request *ureq;
- struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
- struct device *dev = usbhs_priv_to_dev(priv);
ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags);
- if (!ureq) {
- dev_err(dev, "ureq alloc fail\n");
+ if (!ureq)
return NULL;
- }
usbhs_pkt_init(&ureq->pkt);
ureq->urb = urb;
@@ -388,10 +384,8 @@ static int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
unsigned long flags;
uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
- if (!uep) {
- dev_err(dev, "usbhsh_ep alloc fail\n");
+ if (!uep)
return -ENOMEM;
- }
/******************** spin lock ********************/
usbhs_lock(priv, flags);
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index c238772..9396a8c 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -804,10 +804,8 @@ int usbhs_pipe_probe(struct usbhs_priv *priv)
}
info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL);
- if (!info->pipe) {
- dev_err(dev, "Could not allocate pipe\n");
+ if (!info->pipe)
return -ENOMEM;
- }
info->size = pipe_size;
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 4d6a5c6..54a4de0 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -118,6 +118,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x8411) }, /* Kyocera GPS Module */
{ USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
+ { USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
{ USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 4f7e072..e49ad0c 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -30,12 +30,12 @@
#include <linux/usb/ezusb.h>
/* make a simple define to handle if we are compiling keyspan_pda or xircom support */
-#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_KEYSPAN_PDA_MODULE)
+#if IS_ENABLED(CONFIG_USB_SERIAL_KEYSPAN_PDA)
#define KEYSPAN
#else
#undef KEYSPAN
#endif
-#if defined(CONFIG_USB_SERIAL_XIRCOM) || defined(CONFIG_USB_SERIAL_XIRCOM_MODULE)
+#if IS_ENABLED(CONFIG_USB_SERIAL_XIRCOM)
#define XIRCOM
#else
#undef XIRCOM
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index 07b4bf0..a8b9bdb 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -179,23 +179,23 @@
/* Config struct */
struct ti_uart_config {
- __u16 wBaudRate;
- __u16 wFlags;
- __u8 bDataBits;
- __u8 bParity;
- __u8 bStopBits;
+ __be16 wBaudRate;
+ __be16 wFlags;
+ u8 bDataBits;
+ u8 bParity;
+ u8 bStopBits;
char cXon;
char cXoff;
- __u8 bUartMode;
+ u8 bUartMode;
} __packed;
/* Get port status */
struct ti_port_status {
- __u8 bCmdCode;
- __u8 bModuleId;
- __u8 bErrorCode;
- __u8 bMSR;
- __u8 bLSR;
+ u8 bCmdCode;
+ u8 bModuleId;
+ u8 bErrorCode;
+ u8 bMSR;
+ u8 bLSR;
} __packed;
/* Purge modes */
@@ -218,12 +218,12 @@ struct ti_port_status {
#define TI_RW_DATA_DOUBLE_WORD 0x04
struct ti_write_data_bytes {
- __u8 bAddrType;
- __u8 bDataType;
- __u8 bDataCounter;
+ u8 bAddrType;
+ u8 bDataType;
+ u8 bDataCounter;
__be16 wBaseAddrHi;
__be16 wBaseAddrLo;
- __u8 bData[0];
+ u8 bData[0];
} __packed;
struct ti_read_data_request {
@@ -258,7 +258,7 @@ struct ti_interrupt {
/* Firmware image header */
struct ti_firmware_header {
__le16 wLength;
- __u8 bCheckSum;
+ u8 bCheckSum;
} __packed;
/* UART addresses */
@@ -276,9 +276,6 @@ struct ti_firmware_header {
#define TI_DEFAULT_CLOSING_WAIT 4000 /* in .01 secs */
-/* supported setserial flags */
-#define TI_SET_SERIAL_FLAGS 0
-
/* read urb states */
#define TI_READ_URB_RUNNING 0
#define TI_READ_URB_STOPPING 1
@@ -288,11 +285,10 @@ struct ti_firmware_header {
struct ti_port {
int tp_is_open;
- __u8 tp_msr;
- __u8 tp_shadow_mcr;
- __u8 tp_uart_mode; /* 232 or 485 modes */
+ u8 tp_msr;
+ u8 tp_shadow_mcr;
+ u8 tp_uart_mode; /* 232 or 485 modes */
unsigned int tp_uart_base_addr;
- int tp_flags;
struct ti_device *tp_tdev;
struct usb_serial_port *tp_port;
spinlock_t tp_lock;
@@ -306,7 +302,6 @@ struct ti_device {
struct usb_serial *td_serial;
int td_is_3410;
bool td_rs485_only;
- int td_urb_error;
};
static int ti_startup(struct usb_serial *serial);
@@ -343,7 +338,7 @@ static int ti_get_serial_info(struct ti_port *tport,
struct serial_struct __user *ret_arg);
static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
struct serial_struct __user *new_arg);
-static void ti_handle_new_msr(struct ti_port *tport, __u8 msr);
+static void ti_handle_new_msr(struct ti_port *tport, u8 msr);
static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty);
static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty);
@@ -354,7 +349,7 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
__u16 moduleid, __u16 value, __u8 *data, int size);
static int ti_write_byte(struct usb_serial_port *port, struct ti_device *tdev,
- unsigned long addr, __u8 mask, __u8 byte);
+ unsigned long addr, u8 mask, u8 byte);
static int ti_download_firmware(struct ti_device *tdev);
@@ -647,12 +642,11 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
struct urb *urb;
int port_number;
int status;
- __u16 open_settings = (__u8)(TI_PIPE_MODE_CONTINUOUS |
- TI_PIPE_TIMEOUT_ENABLE |
- (TI_TRANSFER_TIMEOUT << 2));
+ u16 open_settings;
- if (tport == NULL)
- return -ENODEV;
+ open_settings = (TI_PIPE_MODE_CONTINUOUS |
+ TI_PIPE_TIMEOUT_ENABLE |
+ (TI_TRANSFER_TIMEOUT << 2));
dev = port->serial->dev;
tdev = tport->tp_tdev;
@@ -686,7 +680,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
if (tty)
ti_set_termios(tty, port, &tty->termios);
- dev_dbg(&port->dev, "%s - sending TI_OPEN_PORT\n", __func__);
status = ti_command_out_sync(tdev, TI_OPEN_PORT,
(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
if (status) {
@@ -695,7 +688,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
goto unlink_int_urb;
}
- dev_dbg(&port->dev, "%s - sending TI_START_PORT\n", __func__);
status = ti_command_out_sync(tdev, TI_START_PORT,
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
if (status) {
@@ -704,7 +696,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
goto unlink_int_urb;
}
- dev_dbg(&port->dev, "%s - sending TI_PURGE_PORT\n", __func__);
status = ti_command_out_sync(tdev, TI_PURGE_PORT,
(__u8)(TI_UART1_PORT + port_number), TI_PURGE_INPUT, NULL, 0);
if (status) {
@@ -728,7 +719,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
if (tty)
ti_set_termios(tty, port, &tty->termios);
- dev_dbg(&port->dev, "%s - sending TI_OPEN_PORT (2)\n", __func__);
status = ti_command_out_sync(tdev, TI_OPEN_PORT,
(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
if (status) {
@@ -737,7 +727,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
goto unlink_int_urb;
}
- dev_dbg(&port->dev, "%s - sending TI_START_PORT (2)\n", __func__);
status = ti_command_out_sync(tdev, TI_START_PORT,
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
if (status) {
@@ -747,7 +736,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
}
/* start read urb */
- dev_dbg(&port->dev, "%s - start read urb\n", __func__);
urb = port->read_urb;
if (!urb) {
dev_err(&port->dev, "%s - no read urb\n", __func__);
@@ -773,7 +761,6 @@ unlink_int_urb:
usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
release_lock:
mutex_unlock(&tdev->td_open_close_lock);
- dev_dbg(&port->dev, "%s - exit %d\n", __func__, status);
return status;
}
@@ -789,8 +776,6 @@ static void ti_close(struct usb_serial_port *port)
tdev = usb_get_serial_data(port->serial);
tport = usb_get_serial_port_data(port);
- if (tdev == NULL || tport == NULL)
- return;
tport->tp_is_open = 0;
@@ -803,7 +788,6 @@ static void ti_close(struct usb_serial_port *port)
port_number = port->port_number;
- dev_dbg(&port->dev, "%s - sending TI_CLOSE_PORT\n", __func__);
status = ti_command_out_sync(tdev, TI_CLOSE_PORT,
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
if (status)
@@ -830,11 +814,10 @@ static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
struct ti_port *tport = usb_get_serial_port_data(port);
if (count == 0) {
- dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
- if (tport == NULL || !tport->tp_is_open)
+ if (!tport->tp_is_open)
return -ENODEV;
count = kfifo_in_locked(&port->write_fifo, data, count,
@@ -852,9 +835,6 @@ static int ti_write_room(struct tty_struct *tty)
int room = 0;
unsigned long flags;
- if (tport == NULL)
- return 0;
-
spin_lock_irqsave(&tport->tp_lock, flags);
room = kfifo_avail(&port->write_fifo);
spin_unlock_irqrestore(&tport->tp_lock, flags);
@@ -871,9 +851,6 @@ static int ti_chars_in_buffer(struct tty_struct *tty)
int chars = 0;
unsigned long flags;
- if (tport == NULL)
- return 0;
-
spin_lock_irqsave(&tport->tp_lock, flags);
chars = kfifo_len(&port->write_fifo);
spin_unlock_irqrestore(&tport->tp_lock, flags);
@@ -900,9 +877,6 @@ static void ti_throttle(struct tty_struct *tty)
struct usb_serial_port *port = tty->driver_data;
struct ti_port *tport = usb_get_serial_port_data(port);
- if (tport == NULL)
- return;
-
if (I_IXOFF(tty) || C_CRTSCTS(tty))
ti_stop_read(tport, tty);
@@ -915,9 +889,6 @@ static void ti_unthrottle(struct tty_struct *tty)
struct ti_port *tport = usb_get_serial_port_data(port);
int status;
- if (tport == NULL)
- return;
-
if (I_IXOFF(tty) || C_CRTSCTS(tty)) {
status = ti_restart_read(tport, tty);
if (status)
@@ -932,16 +903,11 @@ static int ti_ioctl(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct ti_port *tport = usb_get_serial_port_data(port);
- if (tport == NULL)
- return -ENODEV;
-
switch (cmd) {
case TIOCGSERIAL:
- dev_dbg(&port->dev, "%s - TIOCGSERIAL\n", __func__);
return ti_get_serial_info(tport,
(struct serial_struct __user *)arg);
case TIOCSSERIAL:
- dev_dbg(&port->dev, "%s - TIOCSSERIAL\n", __func__);
return ti_set_serial_info(tty, tport,
(struct serial_struct __user *)arg);
}
@@ -959,6 +925,8 @@ static void ti_set_termios(struct tty_struct *tty,
int status;
int port_number = port->port_number;
unsigned int mcr;
+ u16 wbaudrate;
+ u16 wflags = 0;
cflag = tty->termios.c_cflag;
iflag = tty->termios.c_iflag;
@@ -967,21 +935,16 @@ static void ti_set_termios(struct tty_struct *tty,
dev_dbg(&port->dev, "%s - old clfag %08x, old iflag %08x\n", __func__,
old_termios->c_cflag, old_termios->c_iflag);
- if (tport == NULL)
- return;
-
config = kmalloc(sizeof(*config), GFP_KERNEL);
if (!config)
return;
- config->wFlags = 0;
-
/* these flags must be set */
- config->wFlags |= TI_UART_ENABLE_MS_INTS;
- config->wFlags |= TI_UART_ENABLE_AUTO_START_DMA;
- config->bUartMode = (__u8)(tport->tp_uart_mode);
+ wflags |= TI_UART_ENABLE_MS_INTS;
+ wflags |= TI_UART_ENABLE_AUTO_START_DMA;
+ config->bUartMode = tport->tp_uart_mode;
- switch (cflag & CSIZE) {
+ switch (C_CSIZE(tty)) {
case CS5:
config->bDataBits = TI_UART_5_DATA_BITS;
break;
@@ -1000,29 +963,29 @@ static void ti_set_termios(struct tty_struct *tty,
/* CMSPAR isn't supported by this driver */
tty->termios.c_cflag &= ~CMSPAR;
- if (cflag & PARENB) {
- if (cflag & PARODD) {
- config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING;
+ if (C_PARENB(tty)) {
+ if (C_PARODD(tty)) {
+ wflags |= TI_UART_ENABLE_PARITY_CHECKING;
config->bParity = TI_UART_ODD_PARITY;
} else {
- config->wFlags |= TI_UART_ENABLE_PARITY_CHECKING;
+ wflags |= TI_UART_ENABLE_PARITY_CHECKING;
config->bParity = TI_UART_EVEN_PARITY;
}
} else {
- config->wFlags &= ~TI_UART_ENABLE_PARITY_CHECKING;
+ wflags &= ~TI_UART_ENABLE_PARITY_CHECKING;
config->bParity = TI_UART_NO_PARITY;
}
- if (cflag & CSTOPB)
+ if (C_CSTOPB(tty))
config->bStopBits = TI_UART_2_STOP_BITS;
else
config->bStopBits = TI_UART_1_STOP_BITS;
- if (cflag & CRTSCTS) {
+ if (C_CRTSCTS(tty)) {
/* RTS flow control must be off to drop RTS for baud rate B0 */
- if ((cflag & CBAUD) != B0)
- config->wFlags |= TI_UART_ENABLE_RTS_IN;
- config->wFlags |= TI_UART_ENABLE_CTS_OUT;
+ if ((C_BAUD(tty)) != B0)
+ wflags |= TI_UART_ENABLE_RTS_IN;
+ wflags |= TI_UART_ENABLE_CTS_OUT;
} else {
ti_restart_read(tport, tty);
}
@@ -1032,34 +995,34 @@ static void ti_set_termios(struct tty_struct *tty,
config->cXoff = STOP_CHAR(tty);
if (I_IXOFF(tty))
- config->wFlags |= TI_UART_ENABLE_X_IN;
+ wflags |= TI_UART_ENABLE_X_IN;
else
ti_restart_read(tport, tty);
if (I_IXON(tty))
- config->wFlags |= TI_UART_ENABLE_X_OUT;
+ wflags |= TI_UART_ENABLE_X_OUT;
}
baud = tty_get_baud_rate(tty);
if (!baud)
baud = 9600;
if (tport->tp_tdev->td_is_3410)
- config->wBaudRate = (__u16)((923077 + baud/2) / baud);
+ wbaudrate = (923077 + baud/2) / baud;
else
- config->wBaudRate = (__u16)((461538 + baud/2) / baud);
+ wbaudrate = (461538 + baud/2) / baud;
/* FIXME: Should calculate resulting baud here and report it back */
- if ((cflag & CBAUD) != B0)
+ if ((C_BAUD(tty)) != B0)
tty_encode_baud_rate(tty, baud, baud);
dev_dbg(&port->dev,
"%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d\n",
- __func__, baud, config->wBaudRate, config->wFlags,
+ __func__, baud, wbaudrate, wflags,
config->bDataBits, config->bParity, config->bStopBits,
config->cXon, config->cXoff, config->bUartMode);
- cpu_to_be16s(&config->wBaudRate);
- cpu_to_be16s(&config->wFlags);
+ config->wBaudRate = cpu_to_be16(wbaudrate);
+ config->wFlags = cpu_to_be16(wflags);
status = ti_command_out_sync(tport->tp_tdev, TI_SET_CONFIG,
(__u8)(TI_UART1_PORT + port_number), 0, (__u8 *)config,
@@ -1071,7 +1034,7 @@ static void ti_set_termios(struct tty_struct *tty,
/* SET_CONFIG asserts RTS and DTR, reset them correctly */
mcr = tport->tp_shadow_mcr;
/* if baud rate is B0, clear RTS and DTR */
- if ((cflag & CBAUD) == B0)
+ if (C_BAUD(tty) == B0)
mcr &= ~(TI_MCR_DTR | TI_MCR_RTS);
status = ti_set_mcr(tport, mcr);
if (status)
@@ -1092,9 +1055,6 @@ static int ti_tiocmget(struct tty_struct *tty)
unsigned int mcr;
unsigned long flags;
- if (tport == NULL)
- return -ENODEV;
-
spin_lock_irqsave(&tport->tp_lock, flags);
msr = tport->tp_msr;
mcr = tport->tp_shadow_mcr;
@@ -1122,9 +1082,6 @@ static int ti_tiocmset(struct tty_struct *tty,
unsigned int mcr;
unsigned long flags;
- if (tport == NULL)
- return -ENODEV;
-
spin_lock_irqsave(&tport->tp_lock, flags);
mcr = tport->tp_shadow_mcr;
@@ -1155,9 +1112,6 @@ static void ti_break(struct tty_struct *tty, int break_state)
dev_dbg(&port->dev, "%s - state = %d\n", __func__, break_state);
- if (tport == NULL)
- return;
-
status = ti_write_byte(port, tport->tp_tdev,
tport->tp_uart_base_addr + TI_UART_OFFSET_LCR,
TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0);
@@ -1189,7 +1143,7 @@ static void ti_interrupt_callback(struct urb *urb)
int function;
int status = urb->status;
int retval;
- __u8 msr;
+ u8 msr;
switch (status) {
case 0:
@@ -1198,11 +1152,9 @@ static void ti_interrupt_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status);
- tdev->td_urb_error = 1;
return;
default:
dev_err(dev, "%s - nonzero urb status, %d\n", __func__, status);
- tdev->td_urb_error = 1;
goto exit;
}
@@ -1275,12 +1227,10 @@ static void ti_bulk_in_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status);
- tport->tp_tdev->td_urb_error = 1;
return;
default:
dev_err(dev, "%s - nonzero urb status, %d\n",
__func__, status);
- tport->tp_tdev->td_urb_error = 1;
}
if (status == -EPIPE)
@@ -1335,12 +1285,10 @@ static void ti_bulk_out_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
dev_dbg(&port->dev, "%s - urb shutting down, %d\n", __func__, status);
- tport->tp_tdev->td_urb_error = 1;
return;
default:
dev_err_console(port, "%s - nonzero urb status, %d\n",
__func__, status);
- tport->tp_tdev->td_urb_error = 1;
}
/* send any buffered data */
@@ -1490,7 +1438,6 @@ static int ti_get_serial_info(struct ti_port *tport,
ret_serial.type = PORT_16550A;
ret_serial.line = port->minor;
ret_serial.port = port->port_number;
- ret_serial.flags = tport->tp_flags;
ret_serial.xmit_fifo_size = kfifo_size(&port->write_fifo);
ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
ret_serial.closing_wait = cwait;
@@ -1515,14 +1462,13 @@ static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
if (cwait != ASYNC_CLOSING_WAIT_NONE)
cwait = msecs_to_jiffies(10 * new_serial.closing_wait);
- tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS;
tport->tp_port->port.closing_wait = cwait;
return 0;
}
-static void ti_handle_new_msr(struct ti_port *tport, __u8 msr)
+static void ti_handle_new_msr(struct ti_port *tport, u8 msr)
{
struct async_icount *icount;
struct tty_struct *tty;
@@ -1634,8 +1580,8 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
static int ti_write_byte(struct usb_serial_port *port,
- struct ti_device *tdev, unsigned long addr,
- __u8 mask, __u8 byte)
+ struct ti_device *tdev, unsigned long addr,
+ u8 mask, u8 byte)
{
int status;
unsigned int size;
@@ -1679,11 +1625,10 @@ static int ti_do_download(struct usb_device *dev, int pipe,
int len;
for (pos = sizeof(struct ti_firmware_header); pos < size; pos++)
- cs = (__u8)(cs + buffer[pos]);
+ cs = (u8)(cs + buffer[pos]);
header = (struct ti_firmware_header *)buffer;
- header->wLength = cpu_to_le16((__u16)(size
- - sizeof(struct ti_firmware_header)));
+ header->wLength = cpu_to_le16(size - sizeof(*header));
header->bCheckSum = cs;
dev_dbg(&dev->dev, "%s - downloading firmware\n", __func__);
@@ -1701,7 +1646,7 @@ static int ti_download_firmware(struct ti_device *tdev)
{
int status;
int buffer_size;
- __u8 *buffer;
+ u8 *buffer;
struct usb_device *dev = tdev->td_serial->dev;
unsigned int pipe = usb_sndbulkpipe(dev,
tdev->td_serial->port[0]->bulk_out_endpointAddress);
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
index 1d8b03c..878b4b8 100644
--- a/drivers/usb/storage/alauda.c
+++ b/drivers/usb/storage/alauda.c
@@ -939,10 +939,8 @@ static int alauda_read_data(struct us_data *us, unsigned long address,
len = min(sectors, blocksize) * (pagesize + 64);
buffer = kmalloc(len, GFP_NOIO);
- if (buffer == NULL) {
- printk(KERN_WARNING "alauda_read_data: Out of memory\n");
+ if (!buffer)
return USB_STOR_TRANSPORT_ERROR;
- }
/* Figure out the initial LBA and page */
lba = address >> blockshift;
@@ -1033,18 +1031,15 @@ static int alauda_write_data(struct us_data *us, unsigned long address,
len = min(sectors, blocksize) * pagesize;
buffer = kmalloc(len, GFP_NOIO);
- if (buffer == NULL) {
- printk(KERN_WARNING "alauda_write_data: Out of memory\n");
+ if (!buffer)
return USB_STOR_TRANSPORT_ERROR;
- }
/*
* We also need a temporary block buffer, where we read in the old data,
* overwrite parts with the new data, and manipulate the redundancy data
*/
blockbuffer = kmalloc((pagesize + 64) * blocksize, GFP_NOIO);
- if (blockbuffer == NULL) {
- printk(KERN_WARNING "alauda_write_data: Out of memory\n");
+ if (!blockbuffer) {
kfree(buffer);
return USB_STOR_TRANSPORT_ERROR;
}
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 33eb923..8cd2926 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -296,6 +296,14 @@ static int slave_configure(struct scsi_device *sdev)
if (us->fflags & US_FL_BROKEN_FUA)
sdev->broken_fua = 1;
+ /* Some even totally fail to indicate a cache */
+ if (us->fflags & US_FL_ALWAYS_SYNC) {
+ /* don't read caching information */
+ sdev->skip_ms_page_8 = 1;
+ sdev->skip_ms_page_3f = 1;
+ /* assume sync is needed */
+ sdev->wce_default_on = 1;
+ }
} else {
/*
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index c5797fa..3aeaa53 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -766,10 +766,8 @@ sddr09_read_data(struct us_data *us,
len = min(sectors, (unsigned int) info->blocksize) * info->pagesize;
buffer = kmalloc(len, GFP_NOIO);
- if (buffer == NULL) {
- printk(KERN_WARNING "sddr09_read_data: Out of memory\n");
+ if (!buffer)
return -ENOMEM;
- }
// This could be made much more efficient by checking for
// contiguous LBA's. Another exercise left to the student.
@@ -1004,10 +1002,8 @@ sddr09_write_data(struct us_data *us,
pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT);
blocklen = (pagelen << info->blockshift);
blockbuffer = kmalloc(blocklen, GFP_NOIO);
- if (!blockbuffer) {
- printk(KERN_WARNING "sddr09_write_data: Out of memory\n");
+ if (!blockbuffer)
return -ENOMEM;
- }
/*
* Since we don't write the user data directly to the device,
@@ -1017,8 +1013,7 @@ sddr09_write_data(struct us_data *us,
len = min(sectors, (unsigned int) info->blocksize) * info->pagesize;
buffer = kmalloc(len, GFP_NOIO);
- if (buffer == NULL) {
- printk(KERN_WARNING "sddr09_write_data: Out of memory\n");
+ if (!buffer) {
kfree(blockbuffer);
return -ENOMEM;
}
@@ -1241,8 +1236,7 @@ sddr09_read_map(struct us_data *us) {
alloc_blocks = min(numblocks, SDDR09_READ_MAP_BUFSZ >> CONTROL_SHIFT);
alloc_len = (alloc_blocks << CONTROL_SHIFT);
buffer = kmalloc(alloc_len, GFP_NOIO);
- if (buffer == NULL) {
- printk(KERN_WARNING "sddr09_read_map: out of memory\n");
+ if (!buffer) {
result = -1;
goto done;
}
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index aa35392..af3c7ee 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -338,6 +338,13 @@ UNUSUAL_DEV( 0x046b, 0xff40, 0x0100, 0x0100,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_WP_DETECT),
+/* Reported by Egbert Eich <eich@suse.com> */
+UNUSUAL_DEV( 0x0480, 0xd010, 0x0100, 0x9999,
+ "Toshiba",
+ "External USB 3.0",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_ALWAYS_SYNC),
+
/* Patch submitted by Philipp Friedrich <philipp@void.at> */
UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100,
"Kyocera",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index ef2d8cd..2cba13a 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -498,7 +498,8 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 |
US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE |
US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES |
- US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS);
+ US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS |
+ US_FL_ALWAYS_SYNC);
p = quirks;
while (*p) {
@@ -581,6 +582,9 @@ void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
case 'w':
f |= US_FL_NO_WP_DETECT;
break;
+ case 'y':
+ f |= US_FL_ALWAYS_SYNC;
+ break;
/* Ignore unrecognized flag characters */
}
}
@@ -794,10 +798,8 @@ static int usb_stor_acquire_resources(struct us_data *us)
struct task_struct *th;
us->current_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!us->current_urb) {
- usb_stor_dbg(us, "URB allocation failed\n");
+ if (!us->current_urb)
return -ENOMEM;
- }
/*
* Just before we start our control thread, initialize
@@ -1070,17 +1072,17 @@ int usb_stor_probe2(struct us_data *us)
result = usb_stor_acquire_resources(us);
if (result)
goto BadDevice;
+ usb_autopm_get_interface_no_resume(us->pusb_intf);
snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",
dev_name(&us->pusb_intf->dev));
result = scsi_add_host(us_to_host(us), dev);
if (result) {
dev_warn(dev,
"Unable to add the scsi host\n");
- goto BadDevice;
+ goto HostAddErr;
}
/* Submit the delayed_work for SCSI-device scanning */
- usb_autopm_get_interface_no_resume(us->pusb_intf);
set_bit(US_FLIDX_SCAN_PENDING, &us->dflags);
if (delay_use > 0)
@@ -1090,6 +1092,8 @@ int usb_stor_probe2(struct us_data *us)
return 0;
/* We come here if there are any problems */
+HostAddErr:
+ usb_autopm_put_interface_no_suspend(us->pusb_intf);
BadDevice:
usb_stor_dbg(us, "storage_probe() failed\n");
release_everything(us);
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index 545d09b..5133a07 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -499,10 +499,8 @@ static int skel_probe(struct usb_interface *interface,
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- dev_err(&interface->dev, "Out of memory\n");
+ if (!dev)
goto error;
- }
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
mutex_init(&dev->io_mutex);
@@ -526,17 +524,11 @@ static int skel_probe(struct usb_interface *interface,
dev->bulk_in_size = buffer_size;
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
- if (!dev->bulk_in_buffer) {
- dev_err(&interface->dev,
- "Could not allocate bulk_in_buffer\n");
+ if (!dev->bulk_in_buffer)
goto error;
- }
dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->bulk_in_urb) {
- dev_err(&interface->dev,
- "Could not allocate bulk_in_urb\n");
+ if (!dev->bulk_in_urb)
goto error;
- }
}
if (!dev->bulk_out_endpointAddr &&
diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig
index 17646b2..eeefa29 100644
--- a/drivers/usb/usbip/Kconfig
+++ b/drivers/usb/usbip/Kconfig
@@ -1,6 +1,7 @@
config USBIP_CORE
tristate "USB/IP support"
- depends on USB_COMMON && NET
+ depends on NET
+ select USB_COMMON
---help---
This enables pushing USB packets over IP to allow remote
machines direct access to USB devices. It provides the
@@ -24,6 +25,27 @@ config USBIP_VHCI_HCD
To compile this driver as a module, choose M here: the
module will be called vhci-hcd.
+config USBIP_VHCI_HC_PORTS
+ int "Number of ports per USB/IP virtual host controller"
+ range 1 31
+ default 8
+ depends on USBIP_VHCI_HCD
+ ---help---
+ To increase number of ports available for USB/IP virtual
+ host controller driver, this defines number of ports per
+ USB/IP virtual host controller.
+
+config USBIP_VHCI_NR_HCS
+ int "Number of USB/IP virtual host controllers"
+ range 1 128
+ default 1
+ depends on USBIP_VHCI_HCD
+ ---help---
+ To increase number of ports available for USB/IP virtual
+ host controller driver, this defines number of USB/IP
+ virtual host controllers as if adding physical host
+ controllers.
+
config USBIP_HOST
tristate "Host driver"
depends on USBIP_CORE && USB
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index 2df63e3..191b176 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -461,7 +461,6 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
priv->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!priv->urb) {
- dev_err(&udev->dev, "malloc urb\n");
usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC);
return;
}
diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h
index a863a98..88b71c4 100644
--- a/drivers/usb/usbip/vhci.h
+++ b/drivers/usb/usbip/vhci.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015 Nobuo Iwata
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -72,13 +73,25 @@ struct vhci_unlink {
};
/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */
-#define VHCI_NPORTS 8
+#ifdef CONFIG_USBIP_VHCI_HC_PORTS
+#define VHCI_HC_PORTS CONFIG_USBIP_VHCI_HC_PORTS
+#else
+#define VHCI_HC_PORTS 8
+#endif
+
+#ifdef CONFIG_USBIP_VHCI_NR_HCS
+#define VHCI_NR_HCS CONFIG_USBIP_VHCI_NR_HCS
+#else
+#define VHCI_NR_HCS 1
+#endif
+
+#define MAX_STATUS_NAME 16
/* for usb_bus.hcpriv */
struct vhci_hcd {
spinlock_t lock;
- u32 port_status[VHCI_NPORTS];
+ u32 port_status[VHCI_HC_PORTS];
unsigned resuming:1;
unsigned long re_timeout;
@@ -90,14 +103,19 @@ struct vhci_hcd {
* wIndex shows the port number and begins from 1.
* But, the index of this array begins from 0.
*/
- struct vhci_device vdev[VHCI_NPORTS];
+ struct vhci_device vdev[VHCI_HC_PORTS];
};
-extern struct vhci_hcd *the_controller;
-extern const struct attribute_group dev_attr_group;
+extern int vhci_num_controllers;
+extern struct platform_device **vhci_pdevs;
+extern struct attribute_group vhci_attr_group;
/* vhci_hcd.c */
-void rh_port_connect(int rhport, enum usb_device_speed speed);
+void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed);
+
+/* vhci_sysfs.c */
+int vhci_init_attr_group(void);
+void vhci_finish_attr_group(void);
/* vhci_rx.c */
struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum);
@@ -106,9 +124,14 @@ int vhci_rx_loop(void *data);
/* vhci_tx.c */
int vhci_tx_loop(void *data);
-static inline struct vhci_device *port_to_vdev(__u32 port)
+static inline __u32 port_to_rhport(__u32 port)
+{
+ return port % VHCI_HC_PORTS;
+}
+
+static inline int port_to_pdev_nr(__u32 port)
{
- return &the_controller->vdev[port];
+ return port / VHCI_HC_PORTS;
}
static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd)
@@ -116,14 +139,25 @@ static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd)
return (struct vhci_hcd *) (hcd->hcd_priv);
}
+static inline struct device *hcd_dev(struct usb_hcd *hcd)
+{
+ return (hcd)->self.controller;
+}
+
+static inline const char *hcd_name(struct usb_hcd *hcd)
+{
+ return (hcd)->self.bus_name;
+}
+
static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci)
{
return container_of((void *) vhci, struct usb_hcd, hcd_priv);
}
-static inline struct device *vhci_dev(struct vhci_hcd *vhci)
+static inline struct vhci_hcd *vdev_to_vhci(struct vhci_device *vdev)
{
- return vhci_to_hcd(vhci)->self.controller;
+ return container_of(
+ (void *)(vdev - vdev->rhport), struct vhci_hcd, vdev);
}
#endif /* __USBIP_VHCI_H */
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index 2e0450b..03eccf2 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Nobuo Iwata
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -56,7 +57,9 @@ static int vhci_get_frame_number(struct usb_hcd *hcd);
static const char driver_name[] = "vhci_hcd";
static const char driver_desc[] = "USB/IP Virtual Host Controller";
-struct vhci_hcd *the_controller;
+int vhci_num_controllers = VHCI_NR_HCS;
+
+struct platform_device **vhci_pdevs;
static const char * const bit_desc[] = {
"CONNECTION", /*0*/
@@ -119,47 +122,59 @@ static void dump_port_status_diff(u32 prev_status, u32 new_status)
pr_debug("\n");
}
-void rh_port_connect(int rhport, enum usb_device_speed speed)
+void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed)
{
+ struct vhci_hcd *vhci = vdev_to_vhci(vdev);
+ int rhport = vdev->rhport;
+ u32 status;
unsigned long flags;
usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
+
+ status = vhci->port_status[rhport];
- the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION
- | (1 << USB_PORT_FEAT_C_CONNECTION);
+ status |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION);
switch (speed) {
case USB_SPEED_HIGH:
- the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED;
+ status |= USB_PORT_STAT_HIGH_SPEED;
break;
case USB_SPEED_LOW:
- the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED;
+ status |= USB_PORT_STAT_LOW_SPEED;
break;
default:
break;
}
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ vhci->port_status[rhport] = status;
+
+ spin_unlock_irqrestore(&vhci->lock, flags);
- usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
+ usb_hcd_poll_rh_status(vhci_to_hcd(vhci));
}
-static void rh_port_disconnect(int rhport)
+static void rh_port_disconnect(struct vhci_device *vdev)
{
+ struct vhci_hcd *vhci = vdev_to_vhci(vdev);
+ int rhport = vdev->rhport;
+ u32 status;
unsigned long flags;
usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
+
+ status = vhci->port_status[rhport];
+
+ status &= ~USB_PORT_STAT_CONNECTION;
+ status |= (1 << USB_PORT_FEAT_C_CONNECTION);
- the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION;
- the_controller->port_status[rhport] |=
- (1 << USB_PORT_FEAT_C_CONNECTION);
+ vhci->port_status[rhport] = status;
- spin_unlock_irqrestore(&the_controller->lock, flags);
- usb_hcd_poll_rh_status(vhci_to_hcd(the_controller));
+ spin_unlock_irqrestore(&vhci->lock, flags);
+ usb_hcd_poll_rh_status(vhci_to_hcd(vhci));
}
#define PORT_C_MASK \
@@ -188,7 +203,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
int changed = 0;
unsigned long flags;
- retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8);
+ retval = DIV_ROUND_UP(VHCI_HC_PORTS + 1, 8);
memset(buf, 0, retval);
vhci = hcd_to_vhci(hcd);
@@ -200,7 +215,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
}
/* check pseudo status register for each port */
- for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+ for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
if ((vhci->port_status[rhport] & PORT_C_MASK)) {
/* The status of a port has been changed, */
usbip_dbg_vhci_rh("port %d status changed\n", rhport);
@@ -225,7 +240,7 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
desc->bDescLength = 9;
desc->wHubCharacteristics = cpu_to_le16(
HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
- desc->bNbrPorts = VHCI_NPORTS;
+ desc->bNbrPorts = VHCI_HC_PORTS;
desc->u.hs.DeviceRemovable[0] = 0xff;
desc->u.hs.DeviceRemovable[1] = 0xff;
}
@@ -238,7 +253,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
int rhport;
unsigned long flags;
- u32 prev_port_status[VHCI_NPORTS];
+ u32 prev_port_status[VHCI_HC_PORTS];
if (!HCD_HW_ACCESSIBLE(hcd))
return -ETIMEDOUT;
@@ -249,7 +264,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
*/
usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
wIndex);
- if (wIndex > VHCI_NPORTS)
+ if (wIndex > VHCI_HC_PORTS)
pr_err("invalid port number %d\n", wIndex);
rhport = ((__u8)(wIndex & 0x00ff)) - 1;
@@ -315,7 +330,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case GetPortStatus:
usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex);
- if (wIndex > VHCI_NPORTS || wIndex < 1) {
+ if (wIndex > VHCI_HC_PORTS || wIndex < 1) {
pr_err("invalid port number %d\n", wIndex);
retval = -EPIPE;
}
@@ -416,14 +431,27 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
static struct vhci_device *get_vdev(struct usb_device *udev)
{
- int i;
+ struct platform_device *pdev;
+ struct usb_hcd *hcd;
+ struct vhci_hcd *vhci;
+ int pdev_nr, rhport;
if (!udev)
return NULL;
- for (i = 0; i < VHCI_NPORTS; i++)
- if (the_controller->vdev[i].udev == udev)
- return port_to_vdev(i);
+ for (pdev_nr = 0; pdev_nr < vhci_num_controllers; pdev_nr++) {
+ pdev = *(vhci_pdevs + pdev_nr);
+ if (pdev == NULL)
+ continue;
+ hcd = platform_get_drvdata(pdev);
+ if (hcd == NULL)
+ continue;
+ vhci = hcd_to_vhci(hcd);
+ for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
+ if (vhci->vdev[rhport].udev == udev)
+ return &vhci->vdev[rhport];
+ }
+ }
return NULL;
}
@@ -432,6 +460,7 @@ static void vhci_tx_urb(struct urb *urb)
{
struct vhci_device *vdev = get_vdev(urb->dev);
struct vhci_priv *priv;
+ struct vhci_hcd *vhci = vdev_to_vhci(vdev);
unsigned long flags;
if (!vdev) {
@@ -447,7 +476,7 @@ static void vhci_tx_urb(struct urb *urb)
spin_lock_irqsave(&vdev->priv_lock, flags);
- priv->seqnum = atomic_inc_return(&the_controller->seqnum);
+ priv->seqnum = atomic_inc_return(&vhci->seqnum);
if (priv->seqnum == 0xffff)
dev_info(&urb->dev->dev, "seqnum max\n");
@@ -465,7 +494,9 @@ static void vhci_tx_urb(struct urb *urb)
static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
+ struct vhci_hcd *vhci = hcd_to_vhci(hcd);
struct device *dev = &urb->dev->dev;
+ u8 portnum = urb->dev->portnum;
int ret = 0;
struct vhci_device *vdev;
unsigned long flags;
@@ -473,26 +504,30 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
hcd, urb, mem_flags);
+ if (portnum > VHCI_HC_PORTS) {
+ pr_err("invalid port number %d\n", portnum);
+ return -ENODEV;
+ }
+ vdev = &vhci->vdev[portnum-1];
+
/* patch to usb_sg_init() is in 2.5.60 */
BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
if (urb->status != -EINPROGRESS) {
dev_err(dev, "URB already unlinked!, status %d\n", urb->status);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
return urb->status;
}
- vdev = port_to_vdev(urb->dev->portnum-1);
-
/* refuse enqueue for dead connection */
spin_lock(&vdev->ud.lock);
if (vdev->ud.status == VDEV_ST_NULL ||
vdev->ud.status == VDEV_ST_ERROR) {
dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport);
spin_unlock(&vdev->ud.lock);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
return -ENODEV;
}
spin_unlock(&vdev->ud.lock);
@@ -565,17 +600,16 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
out:
vhci_tx_urb(urb);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
return 0;
no_need_xmit:
usb_hcd_unlink_urb_from_ep(hcd, urb);
no_need_unlink:
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
if (!ret)
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller),
- urb, urb->status);
+ usb_hcd_giveback_urb(hcd, urb, urb->status);
return ret;
}
@@ -627,19 +661,20 @@ no_need_unlink:
*/
static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{
+ struct vhci_hcd *vhci = hcd_to_vhci(hcd);
struct vhci_priv *priv;
struct vhci_device *vdev;
unsigned long flags;
pr_info("dequeue a urb %p\n", urb);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
priv = urb->hcpriv;
if (!priv) {
/* URB was never linked! or will be soon given back by
* vhci_rx. */
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
return -EIDRM;
}
@@ -648,7 +683,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
ret = usb_hcd_check_unlink_urb(hcd, urb, status);
if (ret) {
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
return ret;
}
}
@@ -676,10 +711,9 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
usb_hcd_unlink_urb_from_ep(hcd, urb);
- spin_unlock_irqrestore(&the_controller->lock, flags);
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
+ usb_hcd_giveback_urb(vhci_to_hcd(vhci), urb, urb->status);
+ spin_lock_irqsave(&vhci->lock, flags);
} else {
/* tcp connection is alive */
@@ -691,12 +725,12 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC);
if (!unlink) {
spin_unlock(&vdev->priv_lock);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC);
return -ENOMEM;
}
- unlink->seqnum = atomic_inc_return(&the_controller->seqnum);
+ unlink->seqnum = atomic_inc_return(&vhci->seqnum);
if (unlink->seqnum == 0xffff)
pr_info("seqnum max\n");
@@ -712,7 +746,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
spin_unlock(&vdev->priv_lock);
}
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
usbip_dbg_vhci_hc("leave\n");
return 0;
@@ -720,10 +754,12 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
{
+ struct vhci_hcd *vhci = vdev_to_vhci(vdev);
+ struct usb_hcd *hcd = vhci_to_hcd(vhci);
struct vhci_unlink *unlink, *tmp;
unsigned long flags;
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
@@ -752,24 +788,23 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
urb->status = -ENODEV;
- usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
list_del(&unlink->list);
spin_unlock(&vdev->priv_lock);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
+ usb_hcd_giveback_urb(hcd, urb, urb->status);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
spin_lock(&vdev->priv_lock);
kfree(unlink);
}
spin_unlock(&vdev->priv_lock);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
}
/*
@@ -827,7 +862,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
* is actually given back by vhci_rx after receiving its return pdu.
*
*/
- rh_port_disconnect(vdev->rhport);
+ rh_port_disconnect(vdev);
pr_info("disconnect device\n");
}
@@ -866,7 +901,7 @@ static void vhci_device_unusable(struct usbip_device *ud)
static void vhci_device_init(struct vhci_device *vdev)
{
- memset(vdev, 0, sizeof(*vdev));
+ memset(vdev, 0, sizeof(struct vhci_device));
vdev->ud.side = USBIP_VHCI;
vdev->ud.status = VDEV_ST_NULL;
@@ -887,17 +922,34 @@ static void vhci_device_init(struct vhci_device *vdev)
usbip_start_eh(&vdev->ud);
}
+static int hcd_name_to_id(const char *name)
+{
+ char *c;
+ long val;
+ int ret;
+
+ c = strchr(name, '.');
+ if (c == NULL)
+ return 0;
+
+ ret = kstrtol(c+1, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
static int vhci_start(struct usb_hcd *hcd)
{
struct vhci_hcd *vhci = hcd_to_vhci(hcd);
- int rhport;
+ int id, rhport;
int err = 0;
usbip_dbg_vhci_hc("enter vhci_start\n");
/* initialize private data of usb_hcd */
- for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+ for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
struct vhci_device *vdev = &vhci->vdev[rhport];
vhci_device_init(vdev);
@@ -910,11 +962,26 @@ static int vhci_start(struct usb_hcd *hcd)
hcd->power_budget = 0; /* no limit */
hcd->uses_new_polling = 1;
+ id = hcd_name_to_id(hcd_name(hcd));
+ if (id < 0) {
+ pr_err("invalid vhci name %s\n", hcd_name(hcd));
+ return -EINVAL;
+ }
+
/* vhci_hcd is now ready to be controlled through sysfs */
- err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
- if (err) {
- pr_err("create sysfs files\n");
- return err;
+ if (id == 0) {
+ err = vhci_init_attr_group();
+ if (err) {
+ pr_err("init attr group\n");
+ return err;
+ }
+ err = sysfs_create_group(&hcd_dev(hcd)->kobj, &vhci_attr_group);
+ if (err) {
+ pr_err("create sysfs files\n");
+ vhci_finish_attr_group();
+ return err;
+ }
+ pr_info("created sysfs %s\n", hcd_name(hcd));
}
return 0;
@@ -923,15 +990,19 @@ static int vhci_start(struct usb_hcd *hcd)
static void vhci_stop(struct usb_hcd *hcd)
{
struct vhci_hcd *vhci = hcd_to_vhci(hcd);
- int rhport = 0;
+ int id, rhport;
usbip_dbg_vhci_hc("stop VHCI controller\n");
/* 1. remove the userland interface of vhci_hcd */
- sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group);
+ id = hcd_name_to_id(hcd_name(hcd));
+ if (id == 0) {
+ sysfs_remove_group(&hcd_dev(hcd)->kobj, &vhci_attr_group);
+ vhci_finish_attr_group();
+ }
/* 2. shutdown all the ports of vhci_hcd */
- for (rhport = 0; rhport < VHCI_NPORTS; rhport++) {
+ for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) {
struct vhci_device *vdev = &vhci->vdev[rhport];
usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED);
@@ -1025,9 +1096,6 @@ static int vhci_hcd_probe(struct platform_device *pdev)
}
hcd->has_tt = 1;
- /* this is private data for vhci_hcd */
- the_controller = hcd_to_vhci(hcd);
-
/*
* Finish generic HCD structure initialization and register.
* Call the driver's reset() and start() routines.
@@ -1036,7 +1104,6 @@ static int vhci_hcd_probe(struct platform_device *pdev)
if (ret != 0) {
pr_err("usb_add_hcd failed %d\n", ret);
usb_put_hcd(hcd);
- the_controller = NULL;
return ret;
}
@@ -1059,7 +1126,6 @@ static int vhci_hcd_remove(struct platform_device *pdev)
*/
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
- the_controller = NULL;
return 0;
}
@@ -1070,21 +1136,24 @@ static int vhci_hcd_remove(struct platform_device *pdev)
static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
{
struct usb_hcd *hcd;
- int rhport = 0;
+ struct vhci_hcd *vhci;
+ int rhport;
int connected = 0;
int ret = 0;
unsigned long flags;
hcd = platform_get_drvdata(pdev);
+ if (!hcd)
+ return 0;
+ vhci = hcd_to_vhci(hcd);
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
- for (rhport = 0; rhport < VHCI_NPORTS; rhport++)
- if (the_controller->port_status[rhport] &
- USB_PORT_STAT_CONNECTION)
+ for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++)
+ if (vhci->port_status[rhport] & USB_PORT_STAT_CONNECTION)
connected += 1;
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
if (connected > 0) {
dev_info(&pdev->dev,
@@ -1106,6 +1175,8 @@ static int vhci_hcd_resume(struct platform_device *pdev)
dev_dbg(&pdev->dev, "%s\n", __func__);
hcd = platform_get_drvdata(pdev);
+ if (!hcd)
+ return 0;
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
usb_hcd_poll_rh_status(hcd);
@@ -1129,52 +1200,78 @@ static struct platform_driver vhci_driver = {
},
};
-/*
- * The VHCI 'device' is 'virtual'; not a real plug&play hardware.
- * We need to add this virtual device as a platform device arbitrarily:
- * 1. platform_device_register()
- */
-static void the_pdev_release(struct device *dev)
+static int add_platform_device(int id)
{
+ struct platform_device *pdev;
+ int dev_nr;
+
+ if (id == 0)
+ dev_nr = -1;
+ else
+ dev_nr = id;
+
+ pdev = platform_device_register_simple(driver_name, dev_nr, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ *(vhci_pdevs + id) = pdev;
+ return 0;
}
-static struct platform_device the_pdev = {
- /* should be the same name as driver_name */
- .name = driver_name,
- .id = -1,
- .dev = {
- .release = the_pdev_release,
- },
-};
+static void del_platform_devices(void)
+{
+ struct platform_device *pdev;
+ int i;
+
+ for (i = 0; i < vhci_num_controllers; i++) {
+ pdev = *(vhci_pdevs + i);
+ if (pdev != NULL)
+ platform_device_unregister(pdev);
+ *(vhci_pdevs + i) = NULL;
+ }
+ sysfs_remove_link(&platform_bus.kobj, driver_name);
+}
static int __init vhci_hcd_init(void)
{
- int ret;
+ int i, ret;
if (usb_disabled())
return -ENODEV;
+ if (vhci_num_controllers < 1)
+ vhci_num_controllers = 1;
+
+ vhci_pdevs = kcalloc(vhci_num_controllers, sizeof(void *), GFP_KERNEL);
+ if (vhci_pdevs == NULL)
+ return -ENOMEM;
+
ret = platform_driver_register(&vhci_driver);
if (ret)
goto err_driver_register;
- ret = platform_device_register(&the_pdev);
- if (ret)
- goto err_platform_device_register;
+ for (i = 0; i < vhci_num_controllers; i++) {
+ ret = add_platform_device(i);
+ if (ret)
+ goto err_platform_device_register;
+ }
pr_info(DRIVER_DESC " v" USBIP_VERSION "\n");
return ret;
err_platform_device_register:
+ del_platform_devices();
platform_driver_unregister(&vhci_driver);
err_driver_register:
+ kfree(vhci_pdevs);
return ret;
}
static void __exit vhci_hcd_exit(void)
{
- platform_device_unregister(&the_pdev);
+ del_platform_devices();
platform_driver_unregister(&vhci_driver);
+ kfree(vhci_pdevs);
}
module_init(vhci_hcd_init);
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index d656e0e..fc2d319 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -70,6 +70,7 @@ struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum)
static void vhci_recv_ret_submit(struct vhci_device *vdev,
struct usbip_header *pdu)
{
+ struct vhci_hcd *vhci = vdev_to_vhci(vdev);
struct usbip_device *ud = &vdev->ud;
struct urb *urb;
unsigned long flags;
@@ -81,7 +82,7 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
if (!urb) {
pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum);
pr_info("max seqnum %d\n",
- atomic_read(&the_controller->seqnum));
+ atomic_read(&vhci->seqnum));
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
return;
}
@@ -105,11 +106,11 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
- spin_lock_irqsave(&the_controller->lock, flags);
- usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
+ usb_hcd_unlink_urb_from_ep(vhci_to_hcd(vhci), urb);
+ spin_unlock_irqrestore(&vhci->lock, flags);
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status);
+ usb_hcd_giveback_urb(vhci_to_hcd(vhci), urb, urb->status);
usbip_dbg_vhci_rx("Leave\n");
}
@@ -142,6 +143,7 @@ static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev,
static void vhci_recv_ret_unlink(struct vhci_device *vdev,
struct usbip_header *pdu)
{
+ struct vhci_hcd *vhci = vdev_to_vhci(vdev);
struct vhci_unlink *unlink;
struct urb *urb;
unsigned long flags;
@@ -174,12 +176,11 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev,
urb->status = pdu->u.ret_unlink.status;
pr_info("urb->status %d\n", urb->status);
- spin_lock_irqsave(&the_controller->lock, flags);
- usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
+ usb_hcd_unlink_urb_from_ep(vhci_to_hcd(vhci), urb);
+ spin_unlock_irqrestore(&vhci->lock, flags);
- usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
- urb->status);
+ usb_hcd_giveback_urb(vhci_to_hcd(vhci), urb, urb->status);
}
kfree(unlink);
diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c
index 5b5462e..c404017 100644
--- a/drivers/usb/usbip/vhci_sysfs.c
+++ b/drivers/usb/usbip/vhci_sysfs.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2003-2008 Takahiro Hirofuchi
+ * Copyright (C) 2015-2016 Nobuo Iwata
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,6 +21,8 @@
#include <linux/kthread.h>
#include <linux/file.h>
#include <linux/net.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
#include "usbip_common.h"
#include "vhci.h"
@@ -27,106 +30,190 @@
/* TODO: refine locking ?*/
/* Sysfs entry to show port status */
-static ssize_t status_show(struct device *dev, struct device_attribute *attr,
- char *out)
+static ssize_t status_show_vhci(int pdev_nr, char *out)
{
+ struct platform_device *pdev = *(vhci_pdevs + pdev_nr);
+ struct vhci_hcd *vhci;
char *s = out;
int i = 0;
unsigned long flags;
- BUG_ON(!the_controller || !out);
+ if (!pdev || !out) {
+ usbip_dbg_vhci_sysfs("show status error\n");
+ return 0;
+ }
+
+ vhci = hcd_to_vhci(platform_get_drvdata(pdev));
- spin_lock_irqsave(&the_controller->lock, flags);
+ spin_lock_irqsave(&vhci->lock, flags);
/*
* output example:
- * prt sta spd dev socket local_busid
- * 000 004 000 000 c5a7bb80 1-2.3
- * 001 004 000 000 d8cee980 2-3.4
+ * port sta spd dev socket local_busid
+ * 0000 004 000 00000000 c5a7bb80 1-2.3
+ * 0001 004 000 00000000 d8cee980 2-3.4
*
* IP address can be retrieved from a socket pointer address by looking
* up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
* port number and its peer IP address.
*/
- out += sprintf(out,
- "prt sta spd bus dev socket local_busid\n");
-
- for (i = 0; i < VHCI_NPORTS; i++) {
- struct vhci_device *vdev = port_to_vdev(i);
+ for (i = 0; i < VHCI_HC_PORTS; i++) {
+ struct vhci_device *vdev = &vhci->vdev[i];
spin_lock(&vdev->ud.lock);
- out += sprintf(out, "%03u %03u ", i, vdev->ud.status);
+ out += sprintf(out, "%04u %03u ",
+ (pdev_nr * VHCI_HC_PORTS) + i,
+ vdev->ud.status);
if (vdev->ud.status == VDEV_ST_USED) {
out += sprintf(out, "%03u %08x ",
- vdev->speed, vdev->devid);
- out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
- out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
+ vdev->speed, vdev->devid);
+ out += sprintf(out, "%16p %s",
+ vdev->ud.tcp_socket,
+ dev_name(&vdev->udev->dev));
} else {
- out += sprintf(out, "000 000 000 0000000000000000 0-0");
+ out += sprintf(out, "000 00000000 ");
+ out += sprintf(out, "0000000000000000 0-0");
}
out += sprintf(out, "\n");
spin_unlock(&vdev->ud.lock);
}
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
+
+ return out - s;
+}
+
+static ssize_t status_show_not_ready(int pdev_nr, char *out)
+{
+ char *s = out;
+ int i = 0;
+
+ for (i = 0; i < VHCI_HC_PORTS; i++) {
+ out += sprintf(out, "%04u %03u ",
+ (pdev_nr * VHCI_HC_PORTS) + i,
+ VDEV_ST_NOTASSIGNED);
+ out += sprintf(out, "000 00000000 0000000000000000 0-0");
+ out += sprintf(out, "\n");
+ }
+ return out - s;
+}
+
+static int status_name_to_id(const char *name)
+{
+ char *c;
+ long val;
+ int ret;
+
+ c = strchr(name, '.');
+ if (c == NULL)
+ return 0;
+ ret = kstrtol(c+1, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static ssize_t status_show(struct device *dev,
+ struct device_attribute *attr, char *out)
+{
+ char *s = out;
+ int pdev_nr;
+
+ out += sprintf(out,
+ "port sta spd dev socket local_busid\n");
+
+ pdev_nr = status_name_to_id(attr->attr.name);
+ if (pdev_nr < 0)
+ out += status_show_not_ready(pdev_nr, out);
+ else
+ out += status_show_vhci(pdev_nr, out);
+
+ return out - s;
+}
+
+static ssize_t nports_show(struct device *dev, struct device_attribute *attr,
+ char *out)
+{
+ char *s = out;
+
+ out += sprintf(out, "%d\n", VHCI_HC_PORTS * vhci_num_controllers);
return out - s;
}
-static DEVICE_ATTR_RO(status);
+static DEVICE_ATTR_RO(nports);
/* Sysfs entry to shutdown a virtual connection */
-static int vhci_port_disconnect(__u32 rhport)
+static int vhci_port_disconnect(struct vhci_hcd *vhci, __u32 rhport)
{
- struct vhci_device *vdev;
+ struct vhci_device *vdev = &vhci->vdev[rhport];
unsigned long flags;
usbip_dbg_vhci_sysfs("enter\n");
/* lock */
- spin_lock_irqsave(&the_controller->lock, flags);
-
- vdev = port_to_vdev(rhport);
-
+ spin_lock_irqsave(&vhci->lock, flags);
spin_lock(&vdev->ud.lock);
+
if (vdev->ud.status == VDEV_ST_NULL) {
pr_err("not connected %d\n", vdev->ud.status);
/* unlock */
spin_unlock(&vdev->ud.lock);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
return -EINVAL;
}
/* unlock */
spin_unlock(&vdev->ud.lock);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);
return 0;
}
+static int valid_port(__u32 pdev_nr, __u32 rhport)
+{
+ if (pdev_nr >= vhci_num_controllers) {
+ pr_err("pdev %u\n", pdev_nr);
+ return 0;
+ }
+ if (rhport >= VHCI_HC_PORTS) {
+ pr_err("rhport %u\n", rhport);
+ return 0;
+ }
+ return 1;
+}
+
static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- int err;
- __u32 rhport = 0;
+ __u32 port = 0, pdev_nr = 0, rhport = 0;
+ struct usb_hcd *hcd;
+ int ret;
- if (sscanf(buf, "%u", &rhport) != 1)
+ if (kstrtoint(buf, 10, &port) < 0)
return -EINVAL;
- /* check rhport */
- if (rhport >= VHCI_NPORTS) {
- dev_err(dev, "invalid port %u\n", rhport);
+ pdev_nr = port_to_pdev_nr(port);
+ rhport = port_to_rhport(port);
+
+ if (!valid_port(pdev_nr, rhport))
return -EINVAL;
+
+ hcd = platform_get_drvdata(*(vhci_pdevs + pdev_nr));
+ if (hcd == NULL) {
+ dev_err(dev, "port is not ready %u\n", port);
+ return -EAGAIN;
}
- err = vhci_port_disconnect(rhport);
- if (err < 0)
+ ret = vhci_port_disconnect(hcd_to_vhci(hcd), rhport);
+ if (ret < 0)
return -EINVAL;
usbip_dbg_vhci_sysfs("Leave\n");
@@ -135,16 +222,12 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
-/* Sysfs entry to establish a virtual connection */
-static int valid_args(__u32 rhport, enum usb_device_speed speed)
+static int valid_args(__u32 pdev_nr, __u32 rhport, enum usb_device_speed speed)
{
- /* check rhport */
- if (rhport >= VHCI_NPORTS) {
- pr_err("port %u\n", rhport);
- return -EINVAL;
+ if (!valid_port(pdev_nr, rhport)) {
+ return 0;
}
- /* check speed */
switch (speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
@@ -154,12 +237,13 @@ static int valid_args(__u32 rhport, enum usb_device_speed speed)
default:
pr_err("Failed attach request for unsupported USB speed: %s\n",
usb_speed_string(speed));
- return -EINVAL;
+ return 0;
}
- return 0;
+ return 1;
}
+/* Sysfs entry to establish a virtual connection */
/*
* To start a new USB/IP attachment, a userland program needs to setup a TCP
* connection and then write its socket descriptor with remote device
@@ -174,10 +258,12 @@ static int valid_args(__u32 rhport, enum usb_device_speed speed)
static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct vhci_device *vdev;
struct socket *socket;
int sockfd = 0;
- __u32 rhport = 0, devid = 0, speed = 0;
+ __u32 port = 0, pdev_nr = 0, rhport = 0, devid = 0, speed = 0;
+ struct usb_hcd *hcd;
+ struct vhci_hcd *vhci;
+ struct vhci_device *vdev;
int err;
unsigned long flags;
@@ -187,16 +273,28 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
* @devid: unique device identifier in a remote host
* @speed: usb device speed in a remote host
*/
- if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4)
+ if (sscanf(buf, "%u %u %u %u", &port, &sockfd, &devid, &speed) != 4)
return -EINVAL;
+ pdev_nr = port_to_pdev_nr(port);
+ rhport = port_to_rhport(port);
- usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
- rhport, sockfd, devid, speed);
+ usbip_dbg_vhci_sysfs("port(%u) pdev(%d) rhport(%u)\n",
+ port, pdev_nr, rhport);
+ usbip_dbg_vhci_sysfs("sockfd(%u) devid(%u) speed(%u)\n",
+ sockfd, devid, speed);
/* check received parameters */
- if (valid_args(rhport, speed) < 0)
+ if (!valid_args(pdev_nr, rhport, speed))
return -EINVAL;
+ hcd = platform_get_drvdata(*(vhci_pdevs + pdev_nr));
+ if (hcd == NULL) {
+ dev_err(dev, "port %d is not ready\n", port);
+ return -EAGAIN;
+ }
+ vhci = hcd_to_vhci(hcd);
+ vdev = &vhci->vdev[rhport];
+
/* Extract socket from fd. */
socket = sockfd_lookup(sockfd, &err);
if (!socket)
@@ -205,14 +303,13 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
/* now need lock until setting vdev status as used */
/* begin a lock */
- spin_lock_irqsave(&the_controller->lock, flags);
- vdev = port_to_vdev(rhport);
+ spin_lock_irqsave(&vhci->lock, flags);
spin_lock(&vdev->ud.lock);
if (vdev->ud.status != VDEV_ST_NULL) {
/* end of the lock */
spin_unlock(&vdev->ud.lock);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
sockfd_put(socket);
@@ -220,9 +317,10 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
return -EINVAL;
}
- dev_info(dev,
- "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n",
- rhport, sockfd, devid, speed, usb_speed_string(speed));
+ dev_info(dev, "pdev(%u) rhport(%u) sockfd(%d)\n",
+ pdev_nr, rhport, sockfd);
+ dev_info(dev, "devid(%u) speed(%u) speed_str(%s)\n",
+ devid, speed, usb_speed_string(speed));
vdev->devid = devid;
vdev->speed = speed;
@@ -230,26 +328,92 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
vdev->ud.status = VDEV_ST_NOTASSIGNED;
spin_unlock(&vdev->ud.lock);
- spin_unlock_irqrestore(&the_controller->lock, flags);
+ spin_unlock_irqrestore(&vhci->lock, flags);
/* end the lock */
vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx");
vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx");
- rh_port_connect(rhport, speed);
+ rh_port_connect(vdev, speed);
return count;
}
static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach);
-static struct attribute *dev_attrs[] = {
- &dev_attr_status.attr,
- &dev_attr_detach.attr,
- &dev_attr_attach.attr,
- &dev_attr_usbip_debug.attr,
- NULL,
+#define MAX_STATUS_NAME 16
+
+struct status_attr {
+ struct device_attribute attr;
+ char name[MAX_STATUS_NAME+1];
};
-const struct attribute_group dev_attr_group = {
- .attrs = dev_attrs,
+static struct status_attr *status_attrs;
+
+static void set_status_attr(int id)
+{
+ struct status_attr *status;
+
+ status = status_attrs + id;
+ if (id == 0)
+ strcpy(status->name, "status");
+ else
+ snprintf(status->name, MAX_STATUS_NAME+1, "status.%d", id);
+ status->attr.attr.name = status->name;
+ status->attr.attr.mode = S_IRUGO;
+ status->attr.show = status_show;
+}
+
+static int init_status_attrs(void)
+{
+ int id;
+
+ status_attrs = kcalloc(vhci_num_controllers, sizeof(struct status_attr),
+ GFP_KERNEL);
+ if (status_attrs == NULL)
+ return -ENOMEM;
+
+ for (id = 0; id < vhci_num_controllers; id++)
+ set_status_attr(id);
+
+ return 0;
+}
+
+static void finish_status_attrs(void)
+{
+ kfree(status_attrs);
+}
+
+struct attribute_group vhci_attr_group = {
+ .attrs = NULL,
};
+
+int vhci_init_attr_group(void)
+{
+ struct attribute **attrs;
+ int ret, i;
+
+ attrs = kcalloc((vhci_num_controllers + 5), sizeof(struct attribute *),
+ GFP_KERNEL);
+ if (attrs == NULL)
+ return -ENOMEM;
+
+ ret = init_status_attrs();
+ if (ret) {
+ kfree(attrs);
+ return ret;
+ }
+ *attrs = &dev_attr_nports.attr;
+ *(attrs + 1) = &dev_attr_detach.attr;
+ *(attrs + 2) = &dev_attr_attach.attr;
+ *(attrs + 3) = &dev_attr_usbip_debug.attr;
+ for (i = 0; i < vhci_num_controllers; i++)
+ *(attrs + i + 4) = &((status_attrs + i)->attr.attr);
+ vhci_attr_group.attrs = attrs;
+ return 0;
+}
+
+void vhci_finish_attr_group(void)
+{
+ finish_status_attrs();
+ kfree(vhci_attr_group.attrs);
+}
diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c
index 8994a13..7091848 100644
--- a/drivers/usb/usbip/vudc_dev.c
+++ b/drivers/usb/usbip/vudc_dev.c
@@ -450,7 +450,7 @@ static void vudc_shutdown(struct usbip_device *ud)
if (ud->tcp_socket)
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
- if (ud->tcp_tx) {
+ if (ud->tcp_rx) {
kthread_stop_put(ud->tcp_rx);
ud->tcp_rx = NULL;
}
diff --git a/drivers/usb/usbip/vudc_rx.c b/drivers/usb/usbip/vudc_rx.c
index 344bd94..e429b59 100644
--- a/drivers/usb/usbip/vudc_rx.c
+++ b/drivers/usb/usbip/vudc_rx.c
@@ -142,7 +142,7 @@ static int v_recv_cmd_submit(struct vudc *udc,
urb_p->urb->status = -EINPROGRESS;
/* FIXME: more pipe setup to please usbip_common */
- urb_p->urb->pipe &= ~(11 << 30);
+ urb_p->urb->pipe &= ~(3 << 30);
switch (urb_p->ep->type) {
case USB_ENDPOINT_XFER_BULK:
urb_p->urb->pipe |= (PIPE_BULK << 30);
diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c
index da1b872..fb70cbef 100644
--- a/drivers/usb/wusbcore/cbaf.c
+++ b/drivers/usb/wusbcore/cbaf.c
@@ -610,8 +610,7 @@ static int cbaf_probe(struct usb_interface *iface,
cbaf->usb_iface = usb_get_intf(iface);
result = cbaf_check(cbaf);
if (result < 0) {
- dev_err(dev, "This device is not WUSB-CBAF compliant"
- "and is not supported yet.\n");
+ dev_err(dev, "This device is not WUSB-CBAF compliant and is not supported yet.\n");
goto error_check;
}
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c
index 33acd15..79b2b62 100644
--- a/drivers/usb/wusbcore/crypto.c
+++ b/drivers/usb/wusbcore/crypto.c
@@ -229,10 +229,8 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc,
zero_padding = sizeof(struct aes_ccm_block) - zero_padding;
dst_size = blen + sizeof(b0) + sizeof(b1) + zero_padding;
dst_buf = kzalloc(dst_size, GFP_KERNEL);
- if (dst_buf == NULL) {
- printk(KERN_ERR "E: can't alloc destination buffer\n");
+ if (!dst_buf)
goto error_dst_buf;
- }
memset(iv, 0, sizeof(iv));
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c
index b66faaf..8c9421b 100644
--- a/drivers/usb/wusbcore/security.c
+++ b/drivers/usb/wusbcore/security.c
@@ -374,10 +374,8 @@ int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
struct wusb_keydvt_out keydvt_out;
hs = kcalloc(3, sizeof(hs[0]), GFP_KERNEL);
- if (hs == NULL) {
- dev_err(dev, "can't allocate handshake data\n");
+ if (!hs)
goto error_kzalloc;
- }
/* We need to turn encryption before beginning the 4way
* hshake (WUSB1.0[.3.2.2]) */
diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c
index 60a10d2..ed46222 100644
--- a/drivers/usb/wusbcore/wa-nep.c
+++ b/drivers/usb/wusbcore/wa-nep.c
@@ -271,16 +271,11 @@ int wa_nep_create(struct wahc *wa, struct usb_interface *iface)
epd = &iface->cur_altsetting->endpoint[0].desc;
wa->nep_buffer_size = 1024;
wa->nep_buffer = kmalloc(wa->nep_buffer_size, GFP_KERNEL);
- if (wa->nep_buffer == NULL) {
- dev_err(dev,
- "Unable to allocate notification's read buffer\n");
+ if (!wa->nep_buffer)
goto error_nep_buffer;
- }
wa->nep_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (wa->nep_urb == NULL) {
- dev_err(dev, "Unable to allocate notification URB\n");
+ if (wa->nep_urb == NULL)
goto error_urb_alloc;
- }
usb_fill_int_urb(wa->nep_urb, usb_dev,
usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
wa->nep_buffer, wa->nep_buffer_size,
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 69af4fd..167fcc71 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -2865,10 +2865,8 @@ int wa_dti_start(struct wahc *wa)
goto out;
wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (wa->dti_urb == NULL) {
- dev_err(dev, "Can't allocate DTI URB\n");
+ if (wa->dti_urb == NULL)
goto error_dti_urb_alloc;
- }
usb_fill_bulk_urb(
wa->dti_urb, wa->usb_dev,
usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress),
diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c
index 0257f35..0aa6c3c 100644
--- a/drivers/uwb/hwa-rc.c
+++ b/drivers/uwb/hwa-rc.c
@@ -701,10 +701,8 @@ static int hwarc_neep_init(struct uwb_rc *rc)
goto error_rd_buffer;
}
hwarc->neep_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (hwarc->neep_urb == NULL) {
- dev_err(dev, "Unable to allocate notification URB\n");
+ if (hwarc->neep_urb == NULL)
goto error_urb_alloc;
- }
usb_fill_int_urb(hwarc->neep_urb, usb_dev,
usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
hwarc->rd_buffer, PAGE_SIZE,
diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c
index 97fb2f8..3cc98c0 100644
--- a/drivers/vhost/test.c
+++ b/drivers/vhost/test.c
@@ -322,18 +322,7 @@ static struct miscdevice vhost_test_misc = {
"vhost-test",
&vhost_test_fops,
};
-
-static int vhost_test_init(void)
-{
- return misc_register(&vhost_test_misc);
-}
-module_init(vhost_test_init);
-
-static void vhost_test_exit(void)
-{
- misc_deregister(&vhost_test_misc);
-}
-module_exit(vhost_test_exit);
+module_misc_device(vhost_test_misc);
MODULE_VERSION("0.0.1");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/vme/bridges/Kconfig b/drivers/vme/bridges/Kconfig
index f6d8545..f6ddc37 100644
--- a/drivers/vme/bridges/Kconfig
+++ b/drivers/vme/bridges/Kconfig
@@ -13,3 +13,11 @@ config VME_TSI148
help
If you say Y here you get support for the Tundra TSI148 VME bridge
chip.
+
+config VME_FAKE
+ tristate "Fake"
+ help
+ If you say Y here you get support for the fake VME bridge. This
+ provides a virtualised VME Bus for devices with no VME bridge. This
+ is mainly useful for VME development (in the absence of VME
+ hardware).
diff --git a/drivers/vme/bridges/Makefile b/drivers/vme/bridges/Makefile
index 59638af..b074542 100644
--- a/drivers/vme/bridges/Makefile
+++ b/drivers/vme/bridges/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_VME_CA91CX42) += vme_ca91cx42.o
obj-$(CONFIG_VME_TSI148) += vme_tsi148.o
+obj-$(CONFIG_VME_FAKE) += vme_fake.o
diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c
index 9f2c834..6b5ee89 100644
--- a/drivers/vme/bridges/vme_ca91cx42.c
+++ b/drivers/vme/bridges/vme_ca91cx42.c
@@ -47,6 +47,8 @@ static const struct pci_device_id ca91cx42_ids[] = {
{ },
};
+MODULE_DEVICE_TABLE(pci, ca91cx42_ids);
+
static struct pci_driver ca91cx42_driver = {
.name = driver_name,
.id_table = ca91cx42_ids,
@@ -69,7 +71,7 @@ static u32 ca91cx42_LM_irqhandler(struct ca91cx42_driver *bridge, u32 stat)
for (i = 0; i < 4; i++) {
if (stat & CA91CX42_LINT_LM[i]) {
/* We only enable interrupts if the callback is set */
- bridge->lm_callback[i](i);
+ bridge->lm_callback[i](bridge->lm_data[i]);
serviced |= CA91CX42_LINT_LM[i];
}
}
@@ -1410,7 +1412,7 @@ static int ca91cx42_lm_get(struct vme_lm_resource *lm,
* Callback will be passed the monitor triggered.
*/
static int ca91cx42_lm_attach(struct vme_lm_resource *lm, int monitor,
- void (*callback)(int))
+ void (*callback)(void *), void *data)
{
u32 lm_ctl, tmp;
struct ca91cx42_driver *bridge;
@@ -1438,6 +1440,7 @@ static int ca91cx42_lm_attach(struct vme_lm_resource *lm, int monitor,
/* Attach callback */
bridge->lm_callback[monitor] = callback;
+ bridge->lm_data[monitor] = data;
/* Enable Location Monitor interrupt */
tmp = ioread32(bridge->base + LINT_EN);
@@ -1477,6 +1480,7 @@ static int ca91cx42_lm_detach(struct vme_lm_resource *lm, int monitor)
/* Detach callback */
bridge->lm_callback[monitor] = NULL;
+ bridge->lm_data[monitor] = NULL;
/* If all location monitors disabled, disable global Location Monitor */
if ((tmp & (CA91CX42_LINT_LM0 | CA91CX42_LINT_LM1 | CA91CX42_LINT_LM2 |
diff --git a/drivers/vme/bridges/vme_ca91cx42.h b/drivers/vme/bridges/vme_ca91cx42.h
index d54119e..f35c9f5 100644
--- a/drivers/vme/bridges/vme_ca91cx42.h
+++ b/drivers/vme/bridges/vme_ca91cx42.h
@@ -43,7 +43,8 @@ struct ca91cx42_driver {
wait_queue_head_t dma_queue;
wait_queue_head_t iack_queue;
wait_queue_head_t mbox_queue;
- void (*lm_callback[4])(int); /* Called in interrupt handler */
+ void (*lm_callback[4])(void *); /* Called in interrupt handler */
+ void *lm_data[4];
void *crcsr_kernel;
dma_addr_t crcsr_bus;
struct mutex vme_rmw; /* Only one RMW cycle at a time */
diff --git a/drivers/vme/bridges/vme_fake.c b/drivers/vme/bridges/vme_fake.c
new file mode 100644
index 0000000..30b3acc
--- /dev/null
+++ b/drivers/vme/bridges/vme_fake.c
@@ -0,0 +1,1306 @@
+/*
+ * Fake VME bridge support.
+ *
+ * This drive provides a fake VME bridge chip, this enables debugging of the
+ * VME framework in the absence of a VME system.
+ *
+ * This driver has to do a number of things in software that would be driven
+ * by hardware if it was available, it will also result in extra overhead at
+ * times when compared with driving actual hardware.
+ *
+ * Author: Martyn Welch <martyn@welches.me.uk>
+ * Copyright (c) 2014 Martyn Welch
+ *
+ * Based on vme_tsi148.c:
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * Based on work by Tom Armistead and Ajit Prem
+ * Copyright 2004 Motorola Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/vme.h>
+
+#include "../vme_bridge.h"
+
+/*
+ * Define the number of each that the fake driver supports.
+ */
+#define FAKE_MAX_MASTER 8 /* Max Master Windows */
+#define FAKE_MAX_SLAVE 8 /* Max Slave Windows */
+
+/* Structures to hold information normally held in device registers */
+struct fake_slave_window {
+ int enabled;
+ unsigned long long vme_base;
+ unsigned long long size;
+ void *buf_base;
+ u32 aspace;
+ u32 cycle;
+};
+
+struct fake_master_window {
+ int enabled;
+ unsigned long long vme_base;
+ unsigned long long size;
+ u32 aspace;
+ u32 cycle;
+ u32 dwidth;
+};
+
+/* Structure used to hold driver specific information */
+struct fake_driver {
+ struct vme_bridge *parent;
+ struct fake_slave_window slaves[FAKE_MAX_SLAVE];
+ struct fake_master_window masters[FAKE_MAX_MASTER];
+ u32 lm_enabled;
+ unsigned long long lm_base;
+ u32 lm_aspace;
+ u32 lm_cycle;
+ void (*lm_callback[4])(void *);
+ void *lm_data[4];
+ struct tasklet_struct int_tasklet;
+ int int_level;
+ int int_statid;
+ void *crcsr_kernel;
+ dma_addr_t crcsr_bus;
+ /* Only one VME interrupt can be generated at a time, provide locking */
+ struct mutex vme_int;
+};
+
+/* Module parameter */
+static int geoid;
+
+static const char driver_name[] = "vme_fake";
+
+static struct vme_bridge *exit_pointer;
+
+static struct device *vme_root;
+
+/*
+ * Calling VME bus interrupt callback if provided.
+ */
+static void fake_VIRQ_tasklet(unsigned long data)
+{
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *bridge;
+
+ fake_bridge = (struct vme_bridge *) data;
+ bridge = fake_bridge->driver_priv;
+
+ vme_irq_handler(fake_bridge, bridge->int_level, bridge->int_statid);
+}
+
+/*
+ * Configure VME interrupt
+ */
+static void fake_irq_set(struct vme_bridge *fake_bridge, int level,
+ int state, int sync)
+{
+ /* Nothing to do */
+}
+
+static void *fake_pci_to_ptr(dma_addr_t addr)
+{
+ return (void *)(uintptr_t)addr;
+}
+
+static dma_addr_t fake_ptr_to_pci(void *addr)
+{
+ return (dma_addr_t)(uintptr_t)addr;
+}
+
+/*
+ * Generate a VME bus interrupt at the requested level & vector. Wait for
+ * interrupt to be acked.
+ */
+static int fake_irq_generate(struct vme_bridge *fake_bridge, int level,
+ int statid)
+{
+ struct fake_driver *bridge;
+
+ bridge = fake_bridge->driver_priv;
+
+ mutex_lock(&bridge->vme_int);
+
+ bridge->int_level = level;
+
+ bridge->int_statid = statid;
+
+ /*
+ * Schedule tasklet to run VME handler to emulate normal VME interrupt
+ * handler behaviour.
+ */
+ tasklet_schedule(&bridge->int_tasklet);
+
+ mutex_unlock(&bridge->vme_int);
+
+ return 0;
+}
+
+/*
+ * Initialize a slave window with the requested attributes.
+ */
+static int fake_slave_set(struct vme_slave_resource *image, int enabled,
+ unsigned long long vme_base, unsigned long long size,
+ dma_addr_t buf_base, u32 aspace, u32 cycle)
+{
+ unsigned int i, granularity = 0;
+ unsigned long long vme_bound;
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *bridge;
+
+ fake_bridge = image->parent;
+ bridge = fake_bridge->driver_priv;
+
+ i = image->number;
+
+ switch (aspace) {
+ case VME_A16:
+ granularity = 0x10;
+ break;
+ case VME_A24:
+ granularity = 0x1000;
+ break;
+ case VME_A32:
+ granularity = 0x10000;
+ break;
+ case VME_A64:
+ granularity = 0x10000;
+ break;
+ case VME_CRCSR:
+ case VME_USER1:
+ case VME_USER2:
+ case VME_USER3:
+ case VME_USER4:
+ default:
+ pr_err("Invalid address space\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Bound address is a valid address for the window, adjust
+ * accordingly
+ */
+ vme_bound = vme_base + size - granularity;
+
+ if (vme_base & (granularity - 1)) {
+ pr_err("Invalid VME base alignment\n");
+ return -EINVAL;
+ }
+ if (vme_bound & (granularity - 1)) {
+ pr_err("Invalid VME bound alignment\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&image->mtx);
+
+ bridge->slaves[i].enabled = enabled;
+ bridge->slaves[i].vme_base = vme_base;
+ bridge->slaves[i].size = size;
+ bridge->slaves[i].buf_base = fake_pci_to_ptr(buf_base);
+ bridge->slaves[i].aspace = aspace;
+ bridge->slaves[i].cycle = cycle;
+
+ mutex_unlock(&image->mtx);
+
+ return 0;
+}
+
+/*
+ * Get slave window configuration.
+ */
+static int fake_slave_get(struct vme_slave_resource *image, int *enabled,
+ unsigned long long *vme_base, unsigned long long *size,
+ dma_addr_t *buf_base, u32 *aspace, u32 *cycle)
+{
+ unsigned int i;
+ struct fake_driver *bridge;
+
+ bridge = image->parent->driver_priv;
+
+ i = image->number;
+
+ mutex_lock(&image->mtx);
+
+ *enabled = bridge->slaves[i].enabled;
+ *vme_base = bridge->slaves[i].vme_base;
+ *size = bridge->slaves[i].size;
+ *buf_base = fake_ptr_to_pci(bridge->slaves[i].buf_base);
+ *aspace = bridge->slaves[i].aspace;
+ *cycle = bridge->slaves[i].cycle;
+
+ mutex_unlock(&image->mtx);
+
+ return 0;
+}
+
+/*
+ * Set the attributes of an outbound window.
+ */
+static int fake_master_set(struct vme_master_resource *image, int enabled,
+ unsigned long long vme_base, unsigned long long size,
+ u32 aspace, u32 cycle, u32 dwidth)
+{
+ int retval = 0;
+ unsigned int i;
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *bridge;
+
+ fake_bridge = image->parent;
+
+ bridge = fake_bridge->driver_priv;
+
+ /* Verify input data */
+ if (vme_base & 0xFFFF) {
+ pr_err("Invalid VME Window alignment\n");
+ retval = -EINVAL;
+ goto err_window;
+ }
+
+ if (size & 0xFFFF) {
+ pr_err("Invalid size alignment\n");
+ retval = -EINVAL;
+ goto err_window;
+ }
+
+ if ((size == 0) && (enabled != 0)) {
+ pr_err("Size must be non-zero for enabled windows\n");
+ retval = -EINVAL;
+ goto err_window;
+ }
+
+ /* Setup data width */
+ switch (dwidth) {
+ case VME_D8:
+ case VME_D16:
+ case VME_D32:
+ break;
+ default:
+ pr_err("Invalid data width\n");
+ retval = -EINVAL;
+ goto err_dwidth;
+ }
+
+ /* Setup address space */
+ switch (aspace) {
+ case VME_A16:
+ case VME_A24:
+ case VME_A32:
+ case VME_A64:
+ case VME_CRCSR:
+ case VME_USER1:
+ case VME_USER2:
+ case VME_USER3:
+ case VME_USER4:
+ break;
+ default:
+ pr_err("Invalid address space\n");
+ retval = -EINVAL;
+ goto err_aspace;
+ }
+
+ spin_lock(&image->lock);
+
+ i = image->number;
+
+ bridge->masters[i].enabled = enabled;
+ bridge->masters[i].vme_base = vme_base;
+ bridge->masters[i].size = size;
+ bridge->masters[i].aspace = aspace;
+ bridge->masters[i].cycle = cycle;
+ bridge->masters[i].dwidth = dwidth;
+
+ spin_unlock(&image->lock);
+
+ return 0;
+
+err_aspace:
+err_dwidth:
+err_window:
+ return retval;
+
+}
+
+/*
+ * Set the attributes of an outbound window.
+ */
+static int __fake_master_get(struct vme_master_resource *image, int *enabled,
+ unsigned long long *vme_base, unsigned long long *size,
+ u32 *aspace, u32 *cycle, u32 *dwidth)
+{
+ unsigned int i;
+ struct fake_driver *bridge;
+
+ bridge = image->parent->driver_priv;
+
+ i = image->number;
+
+ *enabled = bridge->masters[i].enabled;
+ *vme_base = bridge->masters[i].vme_base;
+ *size = bridge->masters[i].size;
+ *aspace = bridge->masters[i].aspace;
+ *cycle = bridge->masters[i].cycle;
+ *dwidth = bridge->masters[i].dwidth;
+
+ return 0;
+}
+
+
+static int fake_master_get(struct vme_master_resource *image, int *enabled,
+ unsigned long long *vme_base, unsigned long long *size,
+ u32 *aspace, u32 *cycle, u32 *dwidth)
+{
+ int retval;
+
+ spin_lock(&image->lock);
+
+ retval = __fake_master_get(image, enabled, vme_base, size, aspace,
+ cycle, dwidth);
+
+ spin_unlock(&image->lock);
+
+ return retval;
+}
+
+
+static void fake_lm_check(struct fake_driver *bridge, unsigned long long addr,
+ u32 aspace, u32 cycle)
+{
+ struct vme_bridge *fake_bridge;
+ unsigned long long lm_base;
+ u32 lm_aspace, lm_cycle;
+ int i;
+ struct vme_lm_resource *lm;
+ struct list_head *pos = NULL, *n;
+
+ /* Get vme_bridge */
+ fake_bridge = bridge->parent;
+
+ /* Loop through each location monitor resource */
+ list_for_each_safe(pos, n, &fake_bridge->lm_resources) {
+ lm = list_entry(pos, struct vme_lm_resource, list);
+
+ /* If disabled, we're done */
+ if (bridge->lm_enabled == 0)
+ return;
+
+ lm_base = bridge->lm_base;
+ lm_aspace = bridge->lm_aspace;
+ lm_cycle = bridge->lm_cycle;
+
+ /* First make sure that the cycle and address space match */
+ if ((lm_aspace == aspace) && (lm_cycle == cycle)) {
+ for (i = 0; i < lm->monitors; i++) {
+ /* Each location monitor covers 8 bytes */
+ if (((lm_base + (8 * i)) <= addr) &&
+ ((lm_base + (8 * i) + 8) > addr)) {
+ if (bridge->lm_callback[i] != NULL)
+ bridge->lm_callback[i](
+ bridge->lm_data[i]);
+ }
+ }
+ }
+ }
+}
+
+static u8 fake_vmeread8(struct fake_driver *bridge, unsigned long long addr,
+ u32 aspace, u32 cycle)
+{
+ u8 retval = 0xff;
+ int i;
+ unsigned long long start, end, offset;
+ u8 *loc;
+
+ for (i = 0; i < FAKE_MAX_SLAVE; i++) {
+ start = bridge->slaves[i].vme_base;
+ end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
+
+ if (aspace != bridge->slaves[i].aspace)
+ continue;
+
+ if (cycle != bridge->slaves[i].cycle)
+ continue;
+
+ if ((addr >= start) && (addr < end)) {
+ offset = addr - bridge->slaves[i].vme_base;
+ loc = (u8 *)(bridge->slaves[i].buf_base + offset);
+ retval = *loc;
+
+ break;
+ }
+ }
+
+ fake_lm_check(bridge, addr, aspace, cycle);
+
+ return retval;
+}
+
+static u16 fake_vmeread16(struct fake_driver *bridge, unsigned long long addr,
+ u32 aspace, u32 cycle)
+{
+ u16 retval = 0xffff;
+ int i;
+ unsigned long long start, end, offset;
+ u16 *loc;
+
+ for (i = 0; i < FAKE_MAX_SLAVE; i++) {
+ if (aspace != bridge->slaves[i].aspace)
+ continue;
+
+ if (cycle != bridge->slaves[i].cycle)
+ continue;
+
+ start = bridge->slaves[i].vme_base;
+ end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
+
+ if ((addr >= start) && ((addr + 1) < end)) {
+ offset = addr - bridge->slaves[i].vme_base;
+ loc = (u16 *)(bridge->slaves[i].buf_base + offset);
+ retval = *loc;
+
+ break;
+ }
+ }
+
+ fake_lm_check(bridge, addr, aspace, cycle);
+
+ return retval;
+}
+
+static u32 fake_vmeread32(struct fake_driver *bridge, unsigned long long addr,
+ u32 aspace, u32 cycle)
+{
+ u32 retval = 0xffffffff;
+ int i;
+ unsigned long long start, end, offset;
+ u32 *loc;
+
+ for (i = 0; i < FAKE_MAX_SLAVE; i++) {
+ if (aspace != bridge->slaves[i].aspace)
+ continue;
+
+ if (cycle != bridge->slaves[i].cycle)
+ continue;
+
+ start = bridge->slaves[i].vme_base;
+ end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
+
+ if ((addr >= start) && ((addr + 3) < end)) {
+ offset = addr - bridge->slaves[i].vme_base;
+ loc = (u32 *)(bridge->slaves[i].buf_base + offset);
+ retval = *loc;
+
+ break;
+ }
+ }
+
+ fake_lm_check(bridge, addr, aspace, cycle);
+
+ return retval;
+}
+
+static ssize_t fake_master_read(struct vme_master_resource *image, void *buf,
+ size_t count, loff_t offset)
+{
+ int retval;
+ u32 aspace, cycle, dwidth;
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *priv;
+ int i;
+ unsigned long long addr;
+ unsigned int done = 0;
+ unsigned int count32;
+
+ fake_bridge = image->parent;
+
+ priv = fake_bridge->driver_priv;
+
+ i = image->number;
+
+ addr = (unsigned long long)priv->masters[i].vme_base + offset;
+ aspace = priv->masters[i].aspace;
+ cycle = priv->masters[i].cycle;
+ dwidth = priv->masters[i].dwidth;
+
+ spin_lock(&image->lock);
+
+ /* The following code handles VME address alignment. We cannot use
+ * memcpy_xxx here because it may cut data transfers in to 8-bit
+ * cycles when D16 or D32 cycles are required on the VME bus.
+ * On the other hand, the bridge itself assures that the maximum data
+ * cycle configured for the transfer is used and splits it
+ * automatically for non-aligned addresses, so we don't want the
+ * overhead of needlessly forcing small transfers for the entire cycle.
+ */
+ if (addr & 0x1) {
+ *(u8 *)buf = fake_vmeread8(priv, addr, aspace, cycle);
+ done += 1;
+ if (done == count)
+ goto out;
+ }
+ if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
+ if ((addr + done) & 0x2) {
+ if ((count - done) < 2) {
+ *(u8 *)(buf + done) = fake_vmeread8(priv,
+ addr + done, aspace, cycle);
+ done += 1;
+ goto out;
+ } else {
+ *(u16 *)(buf + done) = fake_vmeread16(priv,
+ addr + done, aspace, cycle);
+ done += 2;
+ }
+ }
+ }
+
+ if (dwidth == VME_D32) {
+ count32 = (count - done) & ~0x3;
+ while (done < count32) {
+ *(u32 *)(buf + done) = fake_vmeread32(priv, addr + done,
+ aspace, cycle);
+ done += 4;
+ }
+ } else if (dwidth == VME_D16) {
+ count32 = (count - done) & ~0x3;
+ while (done < count32) {
+ *(u16 *)(buf + done) = fake_vmeread16(priv, addr + done,
+ aspace, cycle);
+ done += 2;
+ }
+ } else if (dwidth == VME_D8) {
+ count32 = (count - done);
+ while (done < count32) {
+ *(u8 *)(buf + done) = fake_vmeread8(priv, addr + done,
+ aspace, cycle);
+ done += 1;
+ }
+
+ }
+
+ if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
+ if ((count - done) & 0x2) {
+ *(u16 *)(buf + done) = fake_vmeread16(priv, addr + done,
+ aspace, cycle);
+ done += 2;
+ }
+ }
+ if ((count - done) & 0x1) {
+ *(u8 *)(buf + done) = fake_vmeread8(priv, addr + done, aspace,
+ cycle);
+ done += 1;
+ }
+
+out:
+ retval = count;
+
+ spin_unlock(&image->lock);
+
+ return retval;
+}
+
+static void fake_vmewrite8(struct fake_driver *bridge, u8 *buf,
+ unsigned long long addr, u32 aspace, u32 cycle)
+{
+ int i;
+ unsigned long long start, end, offset;
+ u8 *loc;
+
+ for (i = 0; i < FAKE_MAX_SLAVE; i++) {
+ if (aspace != bridge->slaves[i].aspace)
+ continue;
+
+ if (cycle != bridge->slaves[i].cycle)
+ continue;
+
+ start = bridge->slaves[i].vme_base;
+ end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
+
+ if ((addr >= start) && (addr < end)) {
+ offset = addr - bridge->slaves[i].vme_base;
+ loc = (u8 *)((void *)bridge->slaves[i].buf_base + offset);
+ *loc = *buf;
+
+ break;
+ }
+ }
+
+ fake_lm_check(bridge, addr, aspace, cycle);
+
+}
+
+static void fake_vmewrite16(struct fake_driver *bridge, u16 *buf,
+ unsigned long long addr, u32 aspace, u32 cycle)
+{
+ int i;
+ unsigned long long start, end, offset;
+ u16 *loc;
+
+ for (i = 0; i < FAKE_MAX_SLAVE; i++) {
+ if (aspace != bridge->slaves[i].aspace)
+ continue;
+
+ if (cycle != bridge->slaves[i].cycle)
+ continue;
+
+ start = bridge->slaves[i].vme_base;
+ end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
+
+ if ((addr >= start) && ((addr + 1) < end)) {
+ offset = addr - bridge->slaves[i].vme_base;
+ loc = (u16 *)((void *)bridge->slaves[i].buf_base + offset);
+ *loc = *buf;
+
+ break;
+ }
+ }
+
+ fake_lm_check(bridge, addr, aspace, cycle);
+
+}
+
+static void fake_vmewrite32(struct fake_driver *bridge, u32 *buf,
+ unsigned long long addr, u32 aspace, u32 cycle)
+{
+ int i;
+ unsigned long long start, end, offset;
+ u32 *loc;
+
+ for (i = 0; i < FAKE_MAX_SLAVE; i++) {
+ if (aspace != bridge->slaves[i].aspace)
+ continue;
+
+ if (cycle != bridge->slaves[i].cycle)
+ continue;
+
+ start = bridge->slaves[i].vme_base;
+ end = bridge->slaves[i].vme_base + bridge->slaves[i].size;
+
+ if ((addr >= start) && ((addr + 3) < end)) {
+ offset = addr - bridge->slaves[i].vme_base;
+ loc = (u32 *)((void *)bridge->slaves[i].buf_base + offset);
+ *loc = *buf;
+
+ break;
+ }
+ }
+
+ fake_lm_check(bridge, addr, aspace, cycle);
+
+}
+
+static ssize_t fake_master_write(struct vme_master_resource *image, void *buf,
+ size_t count, loff_t offset)
+{
+ int retval = 0;
+ u32 aspace, cycle, dwidth;
+ unsigned long long addr;
+ int i;
+ unsigned int done = 0;
+ unsigned int count32;
+
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *bridge;
+
+ fake_bridge = image->parent;
+
+ bridge = fake_bridge->driver_priv;
+
+ i = image->number;
+
+ addr = bridge->masters[i].vme_base + offset;
+ aspace = bridge->masters[i].aspace;
+ cycle = bridge->masters[i].cycle;
+ dwidth = bridge->masters[i].dwidth;
+
+ spin_lock(&image->lock);
+
+ /* Here we apply for the same strategy we do in master_read
+ * function in order to assure the correct cycles.
+ */
+ if (addr & 0x1) {
+ fake_vmewrite8(bridge, (u8 *)buf, addr, aspace, cycle);
+ done += 1;
+ if (done == count)
+ goto out;
+ }
+
+ if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
+ if ((addr + done) & 0x2) {
+ if ((count - done) < 2) {
+ fake_vmewrite8(bridge, (u8 *)(buf + done),
+ addr + done, aspace, cycle);
+ done += 1;
+ goto out;
+ } else {
+ fake_vmewrite16(bridge, (u16 *)(buf + done),
+ addr + done, aspace, cycle);
+ done += 2;
+ }
+ }
+ }
+
+ if (dwidth == VME_D32) {
+ count32 = (count - done) & ~0x3;
+ while (done < count32) {
+ fake_vmewrite32(bridge, (u32 *)(buf + done),
+ addr + done, aspace, cycle);
+ done += 4;
+ }
+ } else if (dwidth == VME_D16) {
+ count32 = (count - done) & ~0x3;
+ while (done < count32) {
+ fake_vmewrite16(bridge, (u16 *)(buf + done),
+ addr + done, aspace, cycle);
+ done += 2;
+ }
+ } else if (dwidth == VME_D8) {
+ count32 = (count - done);
+ while (done < count32) {
+ fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done,
+ aspace, cycle);
+ done += 1;
+ }
+
+ }
+
+ if ((dwidth == VME_D16) || (dwidth == VME_D32)) {
+ if ((count - done) & 0x2) {
+ fake_vmewrite16(bridge, (u16 *)(buf + done),
+ addr + done, aspace, cycle);
+ done += 2;
+ }
+ }
+
+ if ((count - done) & 0x1) {
+ fake_vmewrite8(bridge, (u8 *)(buf + done), addr + done, aspace,
+ cycle);
+ done += 1;
+ }
+
+out:
+ retval = count;
+
+ spin_unlock(&image->lock);
+
+ return retval;
+}
+
+/*
+ * Perform an RMW cycle on the VME bus.
+ *
+ * Requires a previously configured master window, returns final value.
+ */
+static unsigned int fake_master_rmw(struct vme_master_resource *image,
+ unsigned int mask, unsigned int compare, unsigned int swap,
+ loff_t offset)
+{
+ u32 tmp, base;
+ u32 aspace, cycle;
+ int i;
+ struct fake_driver *bridge;
+
+ bridge = image->parent->driver_priv;
+
+ /* Find the PCI address that maps to the desired VME address */
+ i = image->number;
+
+ base = bridge->masters[i].vme_base;
+ aspace = bridge->masters[i].aspace;
+ cycle = bridge->masters[i].cycle;
+
+ /* Lock image */
+ spin_lock(&image->lock);
+
+ /* Read existing value */
+ tmp = fake_vmeread32(bridge, base + offset, aspace, cycle);
+
+ /* Perform check */
+ if ((tmp && mask) == (compare && mask)) {
+ tmp = tmp | (mask | swap);
+ tmp = tmp & (~mask | swap);
+
+ /* Write back */
+ fake_vmewrite32(bridge, &tmp, base + offset, aspace, cycle);
+ }
+
+ /* Unlock image */
+ spin_unlock(&image->lock);
+
+ return tmp;
+}
+
+/*
+ * All 4 location monitors reside at the same base - this is therefore a
+ * system wide configuration.
+ *
+ * This does not enable the LM monitor - that should be done when the first
+ * callback is attached and disabled when the last callback is removed.
+ */
+static int fake_lm_set(struct vme_lm_resource *lm, unsigned long long lm_base,
+ u32 aspace, u32 cycle)
+{
+ int i;
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *bridge;
+
+ fake_bridge = lm->parent;
+
+ bridge = fake_bridge->driver_priv;
+
+ mutex_lock(&lm->mtx);
+
+ /* If we already have a callback attached, we can't move it! */
+ for (i = 0; i < lm->monitors; i++) {
+ if (bridge->lm_callback[i] != NULL) {
+ mutex_unlock(&lm->mtx);
+ pr_err("Location monitor callback attached, can't reset\n");
+ return -EBUSY;
+ }
+ }
+
+ switch (aspace) {
+ case VME_A16:
+ case VME_A24:
+ case VME_A32:
+ case VME_A64:
+ break;
+ default:
+ mutex_unlock(&lm->mtx);
+ pr_err("Invalid address space\n");
+ return -EINVAL;
+ }
+
+ bridge->lm_base = lm_base;
+ bridge->lm_aspace = aspace;
+ bridge->lm_cycle = cycle;
+
+ mutex_unlock(&lm->mtx);
+
+ return 0;
+}
+
+/* Get configuration of the callback monitor and return whether it is enabled
+ * or disabled.
+ */
+static int fake_lm_get(struct vme_lm_resource *lm,
+ unsigned long long *lm_base, u32 *aspace, u32 *cycle)
+{
+ struct fake_driver *bridge;
+
+ bridge = lm->parent->driver_priv;
+
+ mutex_lock(&lm->mtx);
+
+ *lm_base = bridge->lm_base;
+ *aspace = bridge->lm_aspace;
+ *cycle = bridge->lm_cycle;
+
+ mutex_unlock(&lm->mtx);
+
+ return bridge->lm_enabled;
+}
+
+/*
+ * Attach a callback to a specific location monitor.
+ *
+ * Callback will be passed the monitor triggered.
+ */
+static int fake_lm_attach(struct vme_lm_resource *lm, int monitor,
+ void (*callback)(void *), void *data)
+{
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *bridge;
+
+ fake_bridge = lm->parent;
+
+ bridge = fake_bridge->driver_priv;
+
+ mutex_lock(&lm->mtx);
+
+ /* Ensure that the location monitor is configured - need PGM or DATA */
+ if (bridge->lm_cycle == 0) {
+ mutex_unlock(&lm->mtx);
+ pr_err("Location monitor not properly configured\n");
+ return -EINVAL;
+ }
+
+ /* Check that a callback isn't already attached */
+ if (bridge->lm_callback[monitor] != NULL) {
+ mutex_unlock(&lm->mtx);
+ pr_err("Existing callback attached\n");
+ return -EBUSY;
+ }
+
+ /* Attach callback */
+ bridge->lm_callback[monitor] = callback;
+ bridge->lm_data[monitor] = data;
+
+ /* Ensure that global Location Monitor Enable set */
+ bridge->lm_enabled = 1;
+
+ mutex_unlock(&lm->mtx);
+
+ return 0;
+}
+
+/*
+ * Detach a callback function forn a specific location monitor.
+ */
+static int fake_lm_detach(struct vme_lm_resource *lm, int monitor)
+{
+ u32 tmp;
+ int i;
+ struct fake_driver *bridge;
+
+ bridge = lm->parent->driver_priv;
+
+ mutex_lock(&lm->mtx);
+
+ /* Detach callback */
+ bridge->lm_callback[monitor] = NULL;
+ bridge->lm_data[monitor] = NULL;
+
+ /* If all location monitors disabled, disable global Location Monitor */
+ tmp = 0;
+ for (i = 0; i < lm->monitors; i++) {
+ if (bridge->lm_callback[i] != NULL)
+ tmp = 1;
+ }
+
+ if (tmp == 0)
+ bridge->lm_enabled = 0;
+
+ mutex_unlock(&lm->mtx);
+
+ return 0;
+}
+
+/*
+ * Determine Geographical Addressing
+ */
+static int fake_slot_get(struct vme_bridge *fake_bridge)
+{
+ return geoid;
+}
+
+static void *fake_alloc_consistent(struct device *parent, size_t size,
+ dma_addr_t *dma)
+{
+ void *alloc = kmalloc(size, GFP_KERNEL);
+
+ if (alloc != NULL)
+ *dma = fake_ptr_to_pci(alloc);
+
+ return alloc;
+}
+
+static void fake_free_consistent(struct device *parent, size_t size,
+ void *vaddr, dma_addr_t dma)
+{
+ kfree(vaddr);
+/*
+ dma_free_coherent(parent, size, vaddr, dma);
+*/
+}
+
+/*
+ * Configure CR/CSR space
+ *
+ * Access to the CR/CSR can be configured at power-up. The location of the
+ * CR/CSR registers in the CR/CSR address space is determined by the boards
+ * Geographic address.
+ *
+ * Each board has a 512kB window, with the highest 4kB being used for the
+ * boards registers, this means there is a fix length 508kB window which must
+ * be mapped onto PCI memory.
+ */
+static int fake_crcsr_init(struct vme_bridge *fake_bridge)
+{
+ u32 vstat;
+ struct fake_driver *bridge;
+
+ bridge = fake_bridge->driver_priv;
+
+ /* Allocate mem for CR/CSR image */
+ bridge->crcsr_kernel = kzalloc(VME_CRCSR_BUF_SIZE, GFP_KERNEL);
+ bridge->crcsr_bus = fake_ptr_to_pci(bridge->crcsr_kernel);
+ if (bridge->crcsr_kernel == NULL)
+ return -ENOMEM;
+
+ vstat = fake_slot_get(fake_bridge);
+
+ pr_info("CR/CSR Offset: %d\n", vstat);
+
+ return 0;
+}
+
+static void fake_crcsr_exit(struct vme_bridge *fake_bridge)
+{
+ struct fake_driver *bridge;
+
+ bridge = fake_bridge->driver_priv;
+
+ kfree(bridge->crcsr_kernel);
+}
+
+
+static int __init fake_init(void)
+{
+ int retval, i;
+ struct list_head *pos = NULL, *n;
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *fake_device;
+ struct vme_master_resource *master_image;
+ struct vme_slave_resource *slave_image;
+ struct vme_lm_resource *lm;
+
+ /* We need a fake parent device */
+ vme_root = __root_device_register("vme", THIS_MODULE);
+
+ /* If we want to support more than one bridge at some point, we need to
+ * dynamically allocate this so we get one per device.
+ */
+ fake_bridge = kzalloc(sizeof(struct vme_bridge), GFP_KERNEL);
+ if (fake_bridge == NULL) {
+ retval = -ENOMEM;
+ goto err_struct;
+ }
+
+ fake_device = kzalloc(sizeof(struct fake_driver), GFP_KERNEL);
+ if (fake_device == NULL) {
+ retval = -ENOMEM;
+ goto err_driver;
+ }
+
+ fake_bridge->driver_priv = fake_device;
+
+ fake_bridge->parent = vme_root;
+
+ fake_device->parent = fake_bridge;
+
+ /* Initialize wait queues & mutual exclusion flags */
+ mutex_init(&fake_device->vme_int);
+ mutex_init(&fake_bridge->irq_mtx);
+ tasklet_init(&fake_device->int_tasklet, fake_VIRQ_tasklet,
+ (unsigned long) fake_bridge);
+
+ strcpy(fake_bridge->name, driver_name);
+
+ /* Add master windows to list */
+ INIT_LIST_HEAD(&fake_bridge->master_resources);
+ for (i = 0; i < FAKE_MAX_MASTER; i++) {
+ master_image = kmalloc(sizeof(struct vme_master_resource),
+ GFP_KERNEL);
+ if (master_image == NULL) {
+ retval = -ENOMEM;
+ goto err_master;
+ }
+ master_image->parent = fake_bridge;
+ spin_lock_init(&master_image->lock);
+ master_image->locked = 0;
+ master_image->number = i;
+ master_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
+ VME_A64;
+ master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
+ VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
+ VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
+ VME_PROG | VME_DATA;
+ master_image->width_attr = VME_D16 | VME_D32;
+ memset(&master_image->bus_resource, 0,
+ sizeof(struct resource));
+ master_image->kern_base = NULL;
+ list_add_tail(&master_image->list,
+ &fake_bridge->master_resources);
+ }
+
+ /* Add slave windows to list */
+ INIT_LIST_HEAD(&fake_bridge->slave_resources);
+ for (i = 0; i < FAKE_MAX_SLAVE; i++) {
+ slave_image = kmalloc(sizeof(struct vme_slave_resource),
+ GFP_KERNEL);
+ if (slave_image == NULL) {
+ retval = -ENOMEM;
+ goto err_slave;
+ }
+ slave_image->parent = fake_bridge;
+ mutex_init(&slave_image->mtx);
+ slave_image->locked = 0;
+ slave_image->number = i;
+ slave_image->address_attr = VME_A16 | VME_A24 | VME_A32 |
+ VME_A64 | VME_CRCSR | VME_USER1 | VME_USER2 |
+ VME_USER3 | VME_USER4;
+ slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT |
+ VME_2eVME | VME_2eSST | VME_2eSSTB | VME_2eSST160 |
+ VME_2eSST267 | VME_2eSST320 | VME_SUPER | VME_USER |
+ VME_PROG | VME_DATA;
+ list_add_tail(&slave_image->list,
+ &fake_bridge->slave_resources);
+ }
+
+ /* Add location monitor to list */
+ INIT_LIST_HEAD(&fake_bridge->lm_resources);
+ lm = kmalloc(sizeof(struct vme_lm_resource), GFP_KERNEL);
+ if (lm == NULL) {
+ pr_err("Failed to allocate memory for location monitor resource structure\n");
+ retval = -ENOMEM;
+ goto err_lm;
+ }
+ lm->parent = fake_bridge;
+ mutex_init(&lm->mtx);
+ lm->locked = 0;
+ lm->number = 1;
+ lm->monitors = 4;
+ list_add_tail(&lm->list, &fake_bridge->lm_resources);
+
+ fake_bridge->slave_get = fake_slave_get;
+ fake_bridge->slave_set = fake_slave_set;
+ fake_bridge->master_get = fake_master_get;
+ fake_bridge->master_set = fake_master_set;
+ fake_bridge->master_read = fake_master_read;
+ fake_bridge->master_write = fake_master_write;
+ fake_bridge->master_rmw = fake_master_rmw;
+ fake_bridge->irq_set = fake_irq_set;
+ fake_bridge->irq_generate = fake_irq_generate;
+ fake_bridge->lm_set = fake_lm_set;
+ fake_bridge->lm_get = fake_lm_get;
+ fake_bridge->lm_attach = fake_lm_attach;
+ fake_bridge->lm_detach = fake_lm_detach;
+ fake_bridge->slot_get = fake_slot_get;
+ fake_bridge->alloc_consistent = fake_alloc_consistent;
+ fake_bridge->free_consistent = fake_free_consistent;
+
+ pr_info("Board is%s the VME system controller\n",
+ (geoid == 1) ? "" : " not");
+
+ pr_info("VME geographical address is set to %d\n", geoid);
+
+ retval = fake_crcsr_init(fake_bridge);
+ if (retval) {
+ pr_err("CR/CSR configuration failed.\n");
+ goto err_crcsr;
+ }
+
+ retval = vme_register_bridge(fake_bridge);
+ if (retval != 0) {
+ pr_err("Chip Registration failed.\n");
+ goto err_reg;
+ }
+
+ exit_pointer = fake_bridge;
+
+ return 0;
+
+err_reg:
+ fake_crcsr_exit(fake_bridge);
+err_crcsr:
+err_lm:
+ /* resources are stored in link list */
+ list_for_each_safe(pos, n, &fake_bridge->lm_resources) {
+ lm = list_entry(pos, struct vme_lm_resource, list);
+ list_del(pos);
+ kfree(lm);
+ }
+err_slave:
+ /* resources are stored in link list */
+ list_for_each_safe(pos, n, &fake_bridge->slave_resources) {
+ slave_image = list_entry(pos, struct vme_slave_resource, list);
+ list_del(pos);
+ kfree(slave_image);
+ }
+err_master:
+ /* resources are stored in link list */
+ list_for_each_safe(pos, n, &fake_bridge->master_resources) {
+ master_image = list_entry(pos, struct vme_master_resource,
+ list);
+ list_del(pos);
+ kfree(master_image);
+ }
+
+ kfree(fake_device);
+err_driver:
+ kfree(fake_bridge);
+err_struct:
+ return retval;
+
+}
+
+
+static void __exit fake_exit(void)
+{
+ struct list_head *pos = NULL;
+ struct list_head *tmplist;
+ struct vme_master_resource *master_image;
+ struct vme_slave_resource *slave_image;
+ int i;
+ struct vme_bridge *fake_bridge;
+ struct fake_driver *bridge;
+
+ fake_bridge = exit_pointer;
+
+ bridge = fake_bridge->driver_priv;
+
+ pr_debug("Driver is being unloaded.\n");
+
+ /*
+ * Shutdown all inbound and outbound windows.
+ */
+ for (i = 0; i < FAKE_MAX_MASTER; i++)
+ bridge->masters[i].enabled = 0;
+
+ for (i = 0; i < FAKE_MAX_SLAVE; i++)
+ bridge->slaves[i].enabled = 0;
+
+ /*
+ * Shutdown Location monitor.
+ */
+ bridge->lm_enabled = 0;
+
+ vme_unregister_bridge(fake_bridge);
+
+ fake_crcsr_exit(fake_bridge);
+ /* resources are stored in link list */
+ list_for_each_safe(pos, tmplist, &fake_bridge->slave_resources) {
+ slave_image = list_entry(pos, struct vme_slave_resource, list);
+ list_del(pos);
+ kfree(slave_image);
+ }
+
+ /* resources are stored in link list */
+ list_for_each_safe(pos, tmplist, &fake_bridge->master_resources) {
+ master_image = list_entry(pos, struct vme_master_resource,
+ list);
+ list_del(pos);
+ kfree(master_image);
+ }
+
+ kfree(fake_bridge->driver_priv);
+
+ kfree(fake_bridge);
+
+ root_device_unregister(vme_root);
+}
+
+
+MODULE_PARM_DESC(geoid, "Set geographical addressing");
+module_param(geoid, int, 0);
+
+MODULE_DESCRIPTION("Fake VME bridge driver");
+MODULE_LICENSE("GPL");
+
+module_init(fake_init);
+module_exit(fake_exit);
diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c
index 4bc5d45..fc1b634 100644
--- a/drivers/vme/bridges/vme_tsi148.c
+++ b/drivers/vme/bridges/vme_tsi148.c
@@ -50,6 +50,8 @@ static const struct pci_device_id tsi148_ids[] = {
{ },
};
+MODULE_DEVICE_TABLE(pci, tsi148_ids);
+
static struct pci_driver tsi148_driver = {
.name = driver_name,
.id_table = tsi148_ids,
@@ -102,7 +104,7 @@ static u32 tsi148_LM_irqhandler(struct tsi148_driver *bridge, u32 stat)
for (i = 0; i < 4; i++) {
if (stat & TSI148_LCSR_INTS_LMS[i]) {
/* We only enable interrupts if the callback is set */
- bridge->lm_callback[i](i);
+ bridge->lm_callback[i](bridge->lm_data[i]);
serviced |= TSI148_LCSR_INTC_LMC[i];
}
}
@@ -2047,7 +2049,7 @@ static int tsi148_lm_get(struct vme_lm_resource *lm,
* Callback will be passed the monitor triggered.
*/
static int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor,
- void (*callback)(int))
+ void (*callback)(void *), void *data)
{
u32 lm_ctl, tmp;
struct vme_bridge *tsi148_bridge;
@@ -2077,6 +2079,7 @@ static int tsi148_lm_attach(struct vme_lm_resource *lm, int monitor,
/* Attach callback */
bridge->lm_callback[monitor] = callback;
+ bridge->lm_data[monitor] = data;
/* Enable Location Monitor interrupt */
tmp = ioread32be(bridge->base + TSI148_LCSR_INTEN);
@@ -2124,6 +2127,7 @@ static int tsi148_lm_detach(struct vme_lm_resource *lm, int monitor)
/* Detach callback */
bridge->lm_callback[monitor] = NULL;
+ bridge->lm_data[monitor] = NULL;
/* If all location monitors disabled, disable global Location Monitor */
if ((lm_en & (TSI148_LCSR_INTS_LM0S | TSI148_LCSR_INTS_LM1S |
diff --git a/drivers/vme/bridges/vme_tsi148.h b/drivers/vme/bridges/vme_tsi148.h
index f5ed143..0935d85 100644
--- a/drivers/vme/bridges/vme_tsi148.h
+++ b/drivers/vme/bridges/vme_tsi148.h
@@ -38,7 +38,8 @@ struct tsi148_driver {
void __iomem *base; /* Base Address of device registers */
wait_queue_head_t dma_queue[2];
wait_queue_head_t iack_queue;
- void (*lm_callback[4])(int); /* Called in interrupt handler */
+ void (*lm_callback[4])(void *); /* Called in interrupt handler */
+ void *lm_data[4];
void *crcsr_kernel;
dma_addr_t crcsr_bus;
struct vme_master_resource *flush_image;
diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c
index 37ac0a5..15b6407 100644
--- a/drivers/vme/vme.c
+++ b/drivers/vme/vme.c
@@ -13,8 +13,8 @@
* option) any later version.
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/export.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -39,7 +39,6 @@ static unsigned int vme_bus_numbers;
static LIST_HEAD(vme_bus_list);
static DEFINE_MUTEX(vme_buses_lock);
-static void __exit vme_exit(void);
static int __init vme_init(void);
static struct vme_dev *dev_to_vme_dev(struct device *dev)
@@ -1321,7 +1320,7 @@ int vme_lm_get(struct vme_resource *resource, unsigned long long *lm_base,
EXPORT_SYMBOL(vme_lm_get);
int vme_lm_attach(struct vme_resource *resource, int monitor,
- void (*callback)(int))
+ void (*callback)(void *), void *data)
{
struct vme_bridge *bridge = find_bridge(resource);
struct vme_lm_resource *lm;
@@ -1338,7 +1337,7 @@ int vme_lm_attach(struct vme_resource *resource, int monitor,
return -EINVAL;
}
- return bridge->lm_attach(lm, monitor, callback);
+ return bridge->lm_attach(lm, monitor, callback, data);
}
EXPORT_SYMBOL(vme_lm_attach);
@@ -1622,25 +1621,10 @@ static int vme_bus_probe(struct device *dev)
return retval;
}
-static int vme_bus_remove(struct device *dev)
-{
- int retval = -ENODEV;
- struct vme_driver *driver;
- struct vme_dev *vdev = dev_to_vme_dev(dev);
-
- driver = dev->platform_data;
-
- if (driver->remove != NULL)
- retval = driver->remove(vdev);
-
- return retval;
-}
-
struct bus_type vme_bus_type = {
.name = "vme",
.match = vme_bus_match,
.probe = vme_bus_probe,
- .remove = vme_bus_remove,
};
EXPORT_SYMBOL(vme_bus_type);
@@ -1648,11 +1632,4 @@ static int __init vme_init(void)
{
return bus_register(&vme_bus_type);
}
-
-static void __exit vme_exit(void)
-{
- bus_unregister(&vme_bus_type);
-}
-
subsys_initcall(vme_init);
-module_exit(vme_exit);
diff --git a/drivers/vme/vme_bridge.h b/drivers/vme/vme_bridge.h
index cb8246f..2662e91 100644
--- a/drivers/vme/vme_bridge.h
+++ b/drivers/vme/vme_bridge.h
@@ -160,7 +160,8 @@ struct vme_bridge {
int (*lm_set) (struct vme_lm_resource *, unsigned long long, u32, u32);
int (*lm_get) (struct vme_lm_resource *, unsigned long long *, u32 *,
u32 *);
- int (*lm_attach) (struct vme_lm_resource *, int, void (*callback)(int));
+ int (*lm_attach)(struct vme_lm_resource *, int,
+ void (*callback)(void *), void *);
int (*lm_detach) (struct vme_lm_resource *, int);
/* CR/CSR space functions */
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index 581a300..82611f1 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -66,7 +66,7 @@ struct w1_therm_family_data {
/* return the address of the refcnt in the family data */
#define THERM_REFCNT(family_data) \
- (&((struct w1_therm_family_data*)family_data)->refcnt)
+ (&((struct w1_therm_family_data *)family_data)->refcnt)
static int w1_therm_add_slave(struct w1_slave *sl)
{
@@ -81,7 +81,8 @@ static int w1_therm_add_slave(struct w1_slave *sl)
static void w1_therm_remove_slave(struct w1_slave *sl)
{
int refcnt = atomic_sub_return(1, THERM_REFCNT(sl->family_data));
- while(refcnt) {
+
+ while (refcnt) {
msleep(1000);
refcnt = atomic_read(THERM_REFCNT(sl->family_data));
}
@@ -151,8 +152,7 @@ static struct w1_family w1_therm_family_DS1825 = {
.fops = &w1_therm_fops,
};
-struct w1_therm_family_converter
-{
+struct w1_therm_family_converter {
u8 broken;
u16 reserved;
struct w1_family *f;
@@ -293,7 +293,7 @@ static inline int w1_DS18B20_precision(struct device *device, int val)
uint8_t precision_bits;
uint8_t mask = 0x60;
- if(val > 12 || val < 9) {
+ if (val > 12 || val < 9) {
pr_warn("Unsupported precision\n");
return -1;
}
@@ -336,7 +336,8 @@ static inline int w1_DS18B20_precision(struct device *device, int val)
/* read values to only alter precision bits */
w1_write_8(dev, W1_READ_SCRATCHPAD);
- if ((count = w1_read_block(dev, rom, 9)) != 9)
+ count = w1_read_block(dev, rom, 9);
+ if (count != 9)
dev_warn(device, "w1_read_block() returned %u instead of 9.\n", count);
crc = w1_calc_crc8(rom, 8);
@@ -366,6 +367,7 @@ post_unlock:
static inline int w1_DS18B20_convert_temp(u8 rom[9])
{
s16 t = le16_to_cpup((__le16 *)rom);
+
return t*1000/16;
}
@@ -415,7 +417,7 @@ static ssize_t w1_slave_store(struct device *device,
for (i = 0; i < ARRAY_SIZE(w1_therm_families); ++i) {
if (w1_therm_families[i].f->fid == sl->family->fid) {
/* zero value indicates to write current configuration to eeprom */
- if (0 == val)
+ if (val == 0)
ret = w1_therm_families[i].eeprom(device);
else
ret = w1_therm_families[i].precision(device, val);
@@ -439,8 +441,7 @@ static ssize_t w1_slave_show(struct device *device,
if (ret != 0)
goto post_unlock;
- if(!sl->family_data)
- {
+ if (!sl->family_data) {
ret = -ENODEV;
goto pre_unlock;
}
@@ -495,7 +496,8 @@ static ssize_t w1_slave_show(struct device *device,
if (!w1_reset_select_slave(sl)) {
w1_write_8(dev, W1_READ_SCRATCHPAD);
- if ((count = w1_read_block(dev, rom, 9)) != 9) {
+ count = w1_read_block(dev, rom, 9);
+ if (count != 9) {
dev_warn(device, "w1_read_block() "
"returned %u instead of 9.\n",
count);
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index bb34362..e213c67 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -53,8 +53,8 @@ int w1_max_slave_ttl = 10;
module_param_named(timeout, w1_timeout, int, 0);
MODULE_PARM_DESC(timeout, "time in seconds between automatic slave searches");
module_param_named(timeout_us, w1_timeout_us, int, 0);
-MODULE_PARM_DESC(timeout, "time in microseconds between automatic slave"
- " searches");
+MODULE_PARM_DESC(timeout_us,
+ "time in microseconds between automatic slave searches");
/* A search stops when w1_max_slave_count devices have been found in that
* search. The next search will start over and detect the same set of devices
* on a static 1-wire bus. Memory is not allocated based on this number, just
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 1bffe00..50dbaa8 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -152,6 +152,19 @@ config TANGOX_WATCHDOG
This driver can be built as a module. The module name is tangox_wdt.
+config WDAT_WDT
+ tristate "ACPI Watchdog Action Table (WDAT)"
+ depends on ACPI
+ select ACPI_WATCHDOG
+ help
+ This driver adds support for systems with ACPI Watchdog Action
+ Table (WDAT) table. Servers typically have this but it can be
+ found on some desktop machines as well. This driver will take
+ over the native iTCO watchdog driver found on many Intel CPUs.
+
+ To compile this driver as module, choose M here: the module will
+ be called wdat_wdt.
+
config WM831X_WATCHDOG
tristate "WM831x watchdog"
depends on MFD_WM831X
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index c22ad3e..cba0043 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -202,6 +202,7 @@ obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o
obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o
+obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c
index 68952d9..99ebf6e 100644
--- a/drivers/watchdog/pcwd_usb.c
+++ b/drivers/watchdog/pcwd_usb.c
@@ -666,10 +666,8 @@ static int usb_pcwd_probe(struct usb_interface *interface,
/* allocate the urb's */
usb_pcwd->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!usb_pcwd->intr_urb) {
- pr_err("Out of memory\n");
+ if (!usb_pcwd->intr_urb)
goto error;
- }
/* initialise the intr urb's */
usb_fill_int_urb(usb_pcwd->intr_urb, udev, pipe,
diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c
new file mode 100644
index 0000000..e473e3b
--- /dev/null
+++ b/drivers/watchdog/wdat_wdt.c
@@ -0,0 +1,526 @@
+/*
+ * ACPI Hardware Watchdog (WDAT) driver.
+ *
+ * Copyright (C) 2016, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/watchdog.h>
+
+#define MAX_WDAT_ACTIONS ACPI_WDAT_ACTION_RESERVED
+
+/**
+ * struct wdat_instruction - Single ACPI WDAT instruction
+ * @entry: Copy of the ACPI table instruction
+ * @reg: Register the instruction is accessing
+ * @node: Next instruction in action sequence
+ */
+struct wdat_instruction {
+ struct acpi_wdat_entry entry;
+ void __iomem *reg;
+ struct list_head node;
+};
+
+/**
+ * struct wdat_wdt - ACPI WDAT watchdog device
+ * @pdev: Parent platform device
+ * @wdd: Watchdog core device
+ * @period: How long is one watchdog period in ms
+ * @stopped_in_sleep: Is this watchdog stopped by the firmware in S1-S5
+ * @stopped: Was the watchdog stopped by the driver in suspend
+ * @actions: An array of instruction lists indexed by an action number from
+ * the WDAT table. There can be %NULL entries for not implemented
+ * actions.
+ */
+struct wdat_wdt {
+ struct platform_device *pdev;
+ struct watchdog_device wdd;
+ unsigned int period;
+ bool stopped_in_sleep;
+ bool stopped;
+ struct list_head *instructions[MAX_WDAT_ACTIONS];
+};
+
+#define to_wdat_wdt(wdd) container_of(wdd, struct wdat_wdt, wdd)
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static int wdat_wdt_read(struct wdat_wdt *wdat,
+ const struct wdat_instruction *instr, u32 *value)
+{
+ const struct acpi_generic_address *gas = &instr->entry.register_region;
+
+ switch (gas->access_width) {
+ case 1:
+ *value = ioread8(instr->reg);
+ break;
+ case 2:
+ *value = ioread16(instr->reg);
+ break;
+ case 3:
+ *value = ioread32(instr->reg);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(&wdat->pdev->dev, "Read %#x from 0x%08llx\n", *value,
+ gas->address);
+
+ return 0;
+}
+
+static int wdat_wdt_write(struct wdat_wdt *wdat,
+ const struct wdat_instruction *instr, u32 value)
+{
+ const struct acpi_generic_address *gas = &instr->entry.register_region;
+
+ switch (gas->access_width) {
+ case 1:
+ iowrite8((u8)value, instr->reg);
+ break;
+ case 2:
+ iowrite16((u16)value, instr->reg);
+ break;
+ case 3:
+ iowrite32(value, instr->reg);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(&wdat->pdev->dev, "Wrote %#x to 0x%08llx\n", value,
+ gas->address);
+
+ return 0;
+}
+
+static int wdat_wdt_run_action(struct wdat_wdt *wdat, unsigned int action,
+ u32 param, u32 *retval)
+{
+ struct wdat_instruction *instr;
+
+ if (action >= ARRAY_SIZE(wdat->instructions))
+ return -EINVAL;
+
+ if (!wdat->instructions[action])
+ return -EOPNOTSUPP;
+
+ dev_dbg(&wdat->pdev->dev, "Running action %#x\n", action);
+
+ /* Run each instruction sequentially */
+ list_for_each_entry(instr, wdat->instructions[action], node) {
+ const struct acpi_wdat_entry *entry = &instr->entry;
+ const struct acpi_generic_address *gas;
+ u32 flags, value, mask, x, y;
+ bool preserve;
+ int ret;
+
+ gas = &entry->register_region;
+
+ preserve = entry->instruction & ACPI_WDAT_PRESERVE_REGISTER;
+ flags = entry->instruction & ~ACPI_WDAT_PRESERVE_REGISTER;
+ value = entry->value;
+ mask = entry->mask;
+
+ switch (flags) {
+ case ACPI_WDAT_READ_VALUE:
+ ret = wdat_wdt_read(wdat, instr, &x);
+ if (ret)
+ return ret;
+ x >>= gas->bit_offset;
+ x &= mask;
+ if (retval)
+ *retval = x == value;
+ break;
+
+ case ACPI_WDAT_READ_COUNTDOWN:
+ ret = wdat_wdt_read(wdat, instr, &x);
+ if (ret)
+ return ret;
+ x >>= gas->bit_offset;
+ x &= mask;
+ if (retval)
+ *retval = x;
+ break;
+
+ case ACPI_WDAT_WRITE_VALUE:
+ x = value & mask;
+ x <<= gas->bit_offset;
+ if (preserve) {
+ ret = wdat_wdt_read(wdat, instr, &y);
+ if (ret)
+ return ret;
+ y = y & ~(mask << gas->bit_offset);
+ x |= y;
+ }
+ ret = wdat_wdt_write(wdat, instr, x);
+ if (ret)
+ return ret;
+ break;
+
+ case ACPI_WDAT_WRITE_COUNTDOWN:
+ x = param;
+ x &= mask;
+ x <<= gas->bit_offset;
+ if (preserve) {
+ ret = wdat_wdt_read(wdat, instr, &y);
+ if (ret)
+ return ret;
+ y = y & ~(mask << gas->bit_offset);
+ x |= y;
+ }
+ ret = wdat_wdt_write(wdat, instr, x);
+ if (ret)
+ return ret;
+ break;
+
+ default:
+ dev_err(&wdat->pdev->dev, "Unknown instruction: %u\n",
+ flags);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat)
+{
+ int ret;
+
+ /*
+ * WDAT specification says that the watchdog is required to reboot
+ * the system when it fires. However, it also states that it is
+ * recommeded to make it configurable through hardware register. We
+ * enable reboot now if it is configrable, just in case.
+ */
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL);
+ if (ret && ret != -EOPNOTSUPP) {
+ dev_err(&wdat->pdev->dev,
+ "Failed to enable reboot when watchdog triggers\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void wdat_wdt_boot_status(struct wdat_wdt *wdat)
+{
+ u32 boot_status = 0;
+ int ret;
+
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_STATUS, 0, &boot_status);
+ if (ret && ret != -EOPNOTSUPP) {
+ dev_err(&wdat->pdev->dev, "Failed to read boot status\n");
+ return;
+ }
+
+ if (boot_status)
+ wdat->wdd.bootstatus = WDIOF_CARDRESET;
+
+ /* Clear the boot status in case BIOS did not do it */
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, NULL);
+ if (ret && ret != -EOPNOTSUPP)
+ dev_err(&wdat->pdev->dev, "Failed to clear boot status\n");
+}
+
+static void wdat_wdt_set_running(struct wdat_wdt *wdat)
+{
+ u32 running = 0;
+ int ret;
+
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_RUNNING_STATE, 0,
+ &running);
+ if (ret && ret != -EOPNOTSUPP)
+ dev_err(&wdat->pdev->dev, "Failed to read running state\n");
+
+ if (running)
+ set_bit(WDOG_HW_RUNNING, &wdat->wdd.status);
+}
+
+static int wdat_wdt_start(struct watchdog_device *wdd)
+{
+ return wdat_wdt_run_action(to_wdat_wdt(wdd),
+ ACPI_WDAT_SET_RUNNING_STATE, 0, NULL);
+}
+
+static int wdat_wdt_stop(struct watchdog_device *wdd)
+{
+ return wdat_wdt_run_action(to_wdat_wdt(wdd),
+ ACPI_WDAT_SET_STOPPED_STATE, 0, NULL);
+}
+
+static int wdat_wdt_ping(struct watchdog_device *wdd)
+{
+ return wdat_wdt_run_action(to_wdat_wdt(wdd), ACPI_WDAT_RESET, 0, NULL);
+}
+
+static int wdat_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ struct wdat_wdt *wdat = to_wdat_wdt(wdd);
+ unsigned int periods;
+ int ret;
+
+ periods = timeout * 1000 / wdat->period;
+ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_COUNTDOWN, periods, NULL);
+ if (!ret)
+ wdd->timeout = timeout;
+ return ret;
+}
+
+static unsigned int wdat_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+ struct wdat_wdt *wdat = to_wdat_wdt(wdd);
+ u32 periods = 0;
+
+ wdat_wdt_run_action(wdat, ACPI_WDAT_GET_COUNTDOWN, 0, &periods);
+ return periods * wdat->period / 1000;
+}
+
+static const struct watchdog_info wdat_wdt_info = {
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = "wdat_wdt",
+};
+
+static const struct watchdog_ops wdat_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = wdat_wdt_start,
+ .stop = wdat_wdt_stop,
+ .ping = wdat_wdt_ping,
+ .set_timeout = wdat_wdt_set_timeout,
+ .get_timeleft = wdat_wdt_get_timeleft,
+};
+
+static int wdat_wdt_probe(struct platform_device *pdev)
+{
+ const struct acpi_wdat_entry *entries;
+ const struct acpi_table_wdat *tbl;
+ struct wdat_wdt *wdat;
+ struct resource *res;
+ void __iomem **regs;
+ acpi_status status;
+ int i, ret;
+
+ status = acpi_get_table(ACPI_SIG_WDAT, 0,
+ (struct acpi_table_header **)&tbl);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ wdat = devm_kzalloc(&pdev->dev, sizeof(*wdat), GFP_KERNEL);
+ if (!wdat)
+ return -ENOMEM;
+
+ regs = devm_kcalloc(&pdev->dev, pdev->num_resources, sizeof(*regs),
+ GFP_KERNEL);
+ if (!regs)
+ return -ENOMEM;
+
+ /* WDAT specification wants to have >= 1ms period */
+ if (tbl->timer_period < 1)
+ return -EINVAL;
+ if (tbl->min_count > tbl->max_count)
+ return -EINVAL;
+
+ wdat->period = tbl->timer_period;
+ wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count;
+ wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count;
+ wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
+ wdat->wdd.info = &wdat_wdt_info;
+ wdat->wdd.ops = &wdat_wdt_ops;
+ wdat->pdev = pdev;
+
+ /* Request and map all resources */
+ for (i = 0; i < pdev->num_resources; i++) {
+ void __iomem *reg;
+
+ res = &pdev->resource[i];
+ if (resource_type(res) == IORESOURCE_MEM) {
+ reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ } else if (resource_type(res) == IORESOURCE_IO) {
+ reg = devm_ioport_map(&pdev->dev, res->start, 1);
+ if (!reg)
+ return -ENOMEM;
+ } else {
+ dev_err(&pdev->dev, "Unsupported resource\n");
+ return -EINVAL;
+ }
+
+ regs[i] = reg;
+ }
+
+ entries = (struct acpi_wdat_entry *)(tbl + 1);
+ for (i = 0; i < tbl->entries; i++) {
+ const struct acpi_generic_address *gas;
+ struct wdat_instruction *instr;
+ struct list_head *instructions;
+ unsigned int action;
+ struct resource r;
+ int j;
+
+ action = entries[i].action;
+ if (action >= MAX_WDAT_ACTIONS) {
+ dev_dbg(&pdev->dev, "Skipping unknown action: %u\n",
+ action);
+ continue;
+ }
+
+ instr = devm_kzalloc(&pdev->dev, sizeof(*instr), GFP_KERNEL);
+ if (!instr)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&instr->node);
+ instr->entry = entries[i];
+
+ gas = &entries[i].register_region;
+
+ memset(&r, 0, sizeof(r));
+ r.start = gas->address;
+ r.end = r.start + gas->access_width;
+ if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ r.flags = IORESOURCE_MEM;
+ } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ r.flags = IORESOURCE_IO;
+ } else {
+ dev_dbg(&pdev->dev, "Unsupported address space: %d\n",
+ gas->space_id);
+ continue;
+ }
+
+ /* Find the matching resource */
+ for (j = 0; j < pdev->num_resources; j++) {
+ res = &pdev->resource[j];
+ if (resource_contains(res, &r)) {
+ instr->reg = regs[j] + r.start - res->start;
+ break;
+ }
+ }
+
+ if (!instr->reg) {
+ dev_err(&pdev->dev, "I/O resource not found\n");
+ return -EINVAL;
+ }
+
+ instructions = wdat->instructions[action];
+ if (!instructions) {
+ instructions = devm_kzalloc(&pdev->dev,
+ sizeof(*instructions), GFP_KERNEL);
+ if (!instructions)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(instructions);
+ wdat->instructions[action] = instructions;
+ }
+
+ list_add_tail(&instr->node, instructions);
+ }
+
+ wdat_wdt_boot_status(wdat);
+ wdat_wdt_set_running(wdat);
+
+ ret = wdat_wdt_enable_reboot(wdat);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, wdat);
+
+ watchdog_set_nowayout(&wdat->wdd, nowayout);
+ return devm_watchdog_register_device(&pdev->dev, &wdat->wdd);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int wdat_wdt_suspend_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wdat_wdt *wdat = platform_get_drvdata(pdev);
+ int ret;
+
+ if (!watchdog_active(&wdat->wdd))
+ return 0;
+
+ /*
+ * We need to stop the watchdog if firmare is not doing it or if we
+ * are going suspend to idle (where firmware is not involved). If
+ * firmware is stopping the watchdog we kick it here one more time
+ * to give it some time.
+ */
+ wdat->stopped = false;
+ if (acpi_target_system_state() == ACPI_STATE_S0 ||
+ !wdat->stopped_in_sleep) {
+ ret = wdat_wdt_stop(&wdat->wdd);
+ if (!ret)
+ wdat->stopped = true;
+ } else {
+ ret = wdat_wdt_ping(&wdat->wdd);
+ }
+
+ return ret;
+}
+
+static int wdat_wdt_resume_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wdat_wdt *wdat = platform_get_drvdata(pdev);
+ int ret;
+
+ if (!watchdog_active(&wdat->wdd))
+ return 0;
+
+ if (!wdat->stopped) {
+ /*
+ * Looks like the boot firmware reinitializes the watchdog
+ * before it hands off to the OS on resume from sleep so we
+ * stop and reprogram the watchdog here.
+ */
+ ret = wdat_wdt_stop(&wdat->wdd);
+ if (ret)
+ return ret;
+
+ ret = wdat_wdt_set_timeout(&wdat->wdd, wdat->wdd.timeout);
+ if (ret)
+ return ret;
+
+ ret = wdat_wdt_enable_reboot(wdat);
+ if (ret)
+ return ret;
+ }
+
+ return wdat_wdt_start(&wdat->wdd);
+}
+#endif
+
+static const struct dev_pm_ops wdat_wdt_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(wdat_wdt_suspend_noirq,
+ wdat_wdt_resume_noirq)
+};
+
+static struct platform_driver wdat_wdt_driver = {
+ .probe = wdat_wdt_probe,
+ .driver = {
+ .name = "wdat_wdt",
+ .pm = &wdat_wdt_pm_ops,
+ },
+};
+
+module_platform_driver(wdat_wdt_driver);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
+MODULE_DESCRIPTION("ACPI Hardware Watchdog (WDAT) driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:wdat_wdt");