Compare commits

...
18 Commits
Author SHA1 Message Date
tmakin a432aad700 bm92t: revert to inline request_irq 2026-08-28 23:28:13 -05:00
tmakin a409dcbfec xusb: tegra210: add needed reg for b01 2026-08-28 19:17:11 -05:00
Peter De Schrijver 98f77a3a33 clk: tegra: dfll: fix use of tune values
There was some confusion on the use of the tune values. They were
defined in 2 different structs, but only initialzed in one of them.
Remove most of the references in struct tegra_dfll_soc_data and adjust
the code accordingly. Only tune_high_min_millivolts is left, because
this can be adjusted using debugfs. Hence copy the initial value from
the cvb table at boot.
2026-08-28 19:17:11 -05:00
Peter De Schrijver fd2bf45a19 clk: tegra: apply regulator align to Vmin
Apply regulator alignment when using Vmin to determine the lowest possible
rate generated by the DFLL.

Change-Id: I169528614871733e4a7568f3ec740079b0e9d097
Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1584439
GVS: Gerrit_Virtual_Submit
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
2026-08-28 19:17:10 -05:00
tmakin 0c0127fdc3 soc/tegra: fuse: print speedo IDs on info 2026-08-28 19:17:10 -05:00
tmakin 4e346d269a drm/tegra: dsi: detect possible_crtcs
Other platforms allow dsi across multiple heads, but some have
one or more heads disabled. This results in a mismatch between
possible_crtcs and the real crtc mask. Apply the same
`tegra_output_find_possible_crtcs` function to DSI to properly
detect.
2026-08-28 19:17:10 -05:00
Alex Frid 8e4d11a333 soc: tegra: Add support for DFLL Vmin CVB
Added Vmin coefficients to CPU DFLL CVB table, and updated Vmin
calculations during OPP table build accordingly. Changed build function
signature to return final Vmin.

Bug 1906940

Change-Id: I6e19266f865e9a3dda21c9e0f7178feece055ddf
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1565468
Reviewed-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Tested-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: Timo Alho <talho@nvidia.com>
2026-08-28 19:17:10 -05:00
Alex Frid 3543245775 clk: soc: tegra: Add DFLL tune high margin voltage
Added DFLL tune high margin voltage entry in CVB table. This allows to
replace current fixed margin defined as number of DFLL voltage steps
with per-table/per-chip margin in millivolts, and convert it to steps
during DFLL initialization.

Bug 1967884

Change-Id: I56268ae2faf9cc633b9f988cacbfd0ea3ba77ff3
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1576307
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
2026-08-28 19:17:10 -05:00
Alex Frid cbf2e8abe9 clk: soc: tegra: Add DFLL tune1_high parameter
Added support for DFLL tune1_high parameter

Bug 1967884

Change-Id: I0140724a22c43a3b05d39735b7a7faebd4be2908
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1577129
Reviewed-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Tested-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
2026-08-28 19:17:10 -05:00
tmakin acf07bcb94 clk: tegra: add locking support to DFLL
Based on part of https://gitlab.com/nvidia/nv-tegra/linux-4.9/-/commit/4f0b1e6e76f7b0ff8d028c66d34da9f37ca39233
2026-08-28 19:17:09 -05:00
Alex Frid e449ec78a4 clk: soc: tegra: Rename DFLL tune1 to tune1_low
Renamed DFLL tune1 to tune1_low to be consistent with
tune0_low/tune0_high naming convention.

Bug 1967884

Change-Id: If23170305143b05c2918e04691c73fe13b777272
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1576306
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
2026-08-28 19:17:09 -05:00
Vince Hsu 35a4c34d22 clk: tegra: add DFLL dynamic tuning support
This patch is heavily based on the DFLL driver which is
written by Paul Walmsley and used by Tegra124 Chrome
projects currently.

The DFLL output can be fine tuned by the tuning parameters
depending on the voltage threshold. Both of the tuning
parameters and voltage threshold are specified by the
chip characterization. This patch introduces a tuning
timer which controls/monitors the tuning status. When
the DFLL is in open loop, only the tune low parameter
can be used. Once the DFLL enters the close loop, we
can switch to the tune high state if the output voltage
goes higher than the predefined threshold.

Added support for PWM regulator based DFLL.

Bug 200262988

Change-Id: I5ed51fbbf4b18cbd8cced5ab27848817201d8b39
Signed-off-by: Vince Hsu <vinceh@nvidia.com>
Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1565464
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: Timo Alho <talho@nvidia.com>
2026-08-28 19:17:09 -05:00
tmakin e07a012ff0 gpio: tegra: deprecate static allocation 2026-08-28 19:17:09 -05:00
tmakin a87a23eb5b tegra210b01: use tegra210b01 pinctrl 2026-08-28 19:17:09 -05:00
Laxman Dewangan 8530ab4958 pinctrl: tegra210: Add support for LPDR bit
Tegra SOC Tegra210b01 have the support for LPDR configuration
via DT property "nvidia,lpdr".

Add support for configuring this property.

bug 200302268

Change-Id: I314af7f5094de6097f3c5944b28ad91ca61b1fdd
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: http://git-master/r/1469552
Reviewed-on: https://git-master.nvidia.com/r/1557094
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: Pavan Kunapuli <pkunapuli@nvidia.com>
GVS: Gerrit_Virtual_Submit
2026-08-28 19:17:09 -05:00
Maulik Shah bd1802e48c gpio: Use IRQ trigger mask helpers
Use IRQ_TYPE_LEVEL_MASK and IRQ_TYPE_EDGE_BOTH instead of open-coded
trigger type combinations in irqchip callbacks.

No functional change intended.

Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Link: https://patch.msgid.link/20260802-b4b4-irq-trigger-mask-gpio-v1-1-948973636c84@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-08-28 19:17:09 -05:00
Runyu Xiao 75be9aeeac gpio: tegra: do not call pinctrl for GPIO direction
tegra_gpio_direction_input() and tegra_gpio_direction_output() already
program the GPIO controller direction registers directly. The additional
pinctrl_gpio_direction_input/output() calls do not add a Tegra pinctrl
operation, because the Tegra pinmux ops provide GPIO request/free
handling but no gpio_set_direction hook.

The extra call still enters the pinctrl core and takes pctldev->mutex.
Shared GPIO users can call the direction path while holding their
per-line spinlock, so this otherwise redundant pinctrl direction call can
sleep in an atomic context.

This was found by our static analysis tool and then confirmed by manual
review of tegra_gpio_probe(), the Tegra GPIO direction callbacks and the
Tegra pinctrl ops. The reviewed path has a default non-sleeping
struct gpio_chip while the direction callback still enters the pinctrl
mutex path.

A directed runtime validation kept the same non-sleeping chip registration
and drove:

  gpio_shared_proxy_direction_output()
  gpiod_direction_output_raw_commit()
  tegra_gpio_direction_output()
  pinctrl_gpio_direction_output()

Lockdep reported a sleep-in-atomic warning with the shared GPIO spinlock
held and pinctrl_get_device_gpio_range() plus tegra_gpio_direction_output()
on the stack.

Do not mark the whole chip as can_sleep to paper over this: can_sleep
describes whether get()/set() may sleep, and Tegra value access is MMIO.
Remove the redundant pinctrl direction calls and keep pinctrl involvement
in the existing request/free path.

Fixes: 11da905412 ("gpio: tegra: Fix offset of pinctrl calls")
Cc: stable@vger.kernel.org
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
Link: https://patch.msgid.link/20260619152439.1239561-1-runyu.xiao@seu.edu.cn
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
2026-08-28 19:17:08 -05:00
tmakin dd86f157f6 Revert "bm92t: convert to typec fwk"
This reverts commit ee65e16217.
2026-08-28 19:17:08 -05:00
30 changed files with 767 additions and 4923 deletions

No files matched your search

-969
View File
@@ -1,969 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
#include <dt-bindings/mfd/max77620.h>
#include <dt-bindings/gpio/tegra-gpio.h>
#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/soc/tegra-pmc.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/pwm/pwm.h>
#include <dt-bindings/pinctrl/pinctrl-tegra.h>
#include "tegra210.dtsi"
/ {
aliases {
i2c1 = "/i2c@7000c000";
i2c2 = "/i2c@7000c400";
i2c3 = "/i2c@7000c500";
i2c5 = "/i2c@7000d000";
rtc0 = "/i2c@7000d000/max77620@3c";
rtc1 = "/rtc@7000e000";
serial0 = &uarta;
serial1 = &uartb;
serial2 = &uartc;
serial3 = &uartd;
};
chosen {
bootargs = "root=/dev/mmcblk0p2 rw rootwait fbcon=rotate:3 printk.synchronous=1 loglevel=4 console=fb0 console=ttyGS0 earlycon";
stdout-path = "serial1:115200n8";
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
vdd-cpu-supply = <&cpu_max_reg>;
};
idle-states {
cpu-sleep {
status = "okay";
};
};
};
gpu@57000000 {
status = "okay";
vdd-supply = <&gpu_max_reg>;
};
pcie@1003000 {
status = "okay";
avdd-pll-uerefe-supply = <&max77620_ldo1>;
hvddio-pex-supply = <&max77620_sd3>;
dvddio-pex-supply = <&max77620_ldo1>;
dvdd-pex-pll-supply = <&max77620_ldo1>;
hvdd-pex-pll-e-supply = <&max77620_sd3>;
vddio-pex-ctl-supply = <&max77620_sd3>;
pci@1,0 {
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-3}>, /* Referred to plat config */
<&{/padctl@7009f000/pads/pcie/lanes/pcie-2}>;
phy-names = "pcie-0", "pcie-1";
nvidia,num-lanes = <2>;
status = "disabled";
};
pci@2,0 {
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-0}>;
phy-names = "pcie-0";
status = "okay";
};
};
host1x@50000000 {
dsia: dsi@54300000 {
status = "okay";
panel@0 {
compatible = "nintendo,panel-nx-dsi";
reset-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_HIGH>;
backlight = <&backlight>;
vdd1-supply = <&v_pavdd_5v0>;
vdd2-supply = <&v_navdd_5v0>;
};
};
/* tegradc.0: DSI */
dc@54200000 {
status = "okay";
pavdd_lcd-supply = <&v_pavdd_5v0>;
navdd_lcd-supply = <&v_navdd_5v0>;
dvdd_lcd-supply = <&max77620_sd3>;
vdd_lcd_bl_en-supply = <&lcd_bl_en>;
nvidia,outputs = <&dsia>;
};
/* tegradc.1: DP */
dc@54240000 {
status = "okay";
avdd_hdmi-supply = <&max77620_ldo8>; /* 1V05 */
avdd_hdmi_pll-supply = <&max77620_sd3>; /* 1V8 */
// vdd_hdmi_5v0-supply = <&vdd_hdmi>; /* 5V0 GPIO_PCC7 fixed reg */
vdd-dp-pwr-supply = <&vdd_3v3>;
avdd-dp-pll-supply = <&max77620_sd3>;
vdd-dp-pad-supply = <&max77620_ldo8>;
extcon-cables = <&bm92t 3>;
extcon-cable-names = "typec1";
nvidia,outputs = <&sor1>;
};
dpaux1: dpaux@54040000 {
status = "okay";
vdd-supply = <&v_pavdd_5v0>;
};
sor1: sor@54580000 {
status = "okay";
nvidia,dpaux = <&dpaux1>;
avdd-io-hdmi-dp-supply = <&max77620_ldo8>;
vdd-hdmi-dp-pll-supply = <&max77620_sd3>;
nvidia,hpd-gpio = <&gpio TEGRA_GPIO(CC, 1) GPIO_ACTIVE_LOW>;
};
vi: i2c@546c0000 {
status = "okay";
};
};
clock@70110000 {
status = "okay";
nvidia,cf = <6>;
nvidia,ci = <0>;
nvidia,cg = <2>;
nvidia,droop-ctrl = <0x00000f00>;
nvidia,force-mode = <1>;
nvidia,i2c-fs-rate = <400000>;
nvidia,sample-rate = <12500>;
vdd-cpu-supply = <&cpu_max_reg>;
};
memory {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x80000000>;
};
reserved-memory {
/delete-node/ vpr-carveout;
/delete-node/ iram-carveout;
};
r2p {
compatible = "tegra-r2p";
status = "okay";
};
/* debug port */
serial@70006000 {
status = "disabled";
};
/* joycon ports:
invert TXD and RTS via IRDA_CSR
start @ 1000000 baud, switch to 3000000
when they are >= 3000000 baud, use 2 stop bits
use flow control
Note: hsuart driver is required for flow control
*/
/* right joycon */
serial@70006040 {
status = "okay";
compatible = "nvidia,tegra124-hsuart", "nvidia,tegra30-hsuart";
nvidia,invert-txd;
nvidia,invert-rts;
reset-names = "serial";
joyconr {
status = "okay";
compatible = "nintendo,joycon-serdev";
charger-supply = <&en_vdd_jcr_chgr>;
detect-en-gpios = <&gpio TEGRA_GPIO(G, 0) GPIO_ACTIVE_HIGH>;
detect-gpios = <&gpio TEGRA_GPIO(H, 6) GPIO_ACTIVE_LOW>;
};
};
/* left joycon */
serial@70006200 {
status = "okay";
compatible = "nvidia,tegra124-hsuart", "nvidia,tegra30-hsuart";
nvidia,invert-txd;
nvidia,invert-rts;
reset-names = "serial";
joyconl {
status = "okay";
compatible = "nintendo,joycon-serdev";
charger-supply = <&en_vdd_jcl_chgr>;
detect-en-gpios = <&gpio TEGRA_GPIO(D, 1) GPIO_ACTIVE_HIGH>;
detect-gpios = <&gpio TEGRA_GPIO(E, 6) GPIO_ACTIVE_LOW>;
};
};
/* bluetooth */
serial@70006300 {
dma-names = "tx";
status = "okay";
compatible = "nvidia,tegra124-hsuart", "nvidia,tegra30-hsuart";
reset-names = "serial";
bluetooth {
compatible = "brcm,bcm43438-bt";
vbat-supply = <&battery_reg>;
vddio-supply = <&battery_reg>;
shutdown-gpios = <&gpio TEGRA_GPIO(H, 4) GPIO_ACTIVE_HIGH>;
device-wakeup-gpios = <&gpio TEGRA_GPIO(H, 3) GPIO_ACTIVE_LOW>;
max-speed = <3000000>; /* Max supported 4 MBaud */
sco-routing = <1>; /* Over HCI */
};
};
clk32k_in: clock-32k {
compatible = "fixed-clock";
clock-frequency = <32768>;
#clock-cells = <0>;
};
backlight: backlight {
compatible = "pwm-backlight";
pwms = <&pwm 0 33898>;
pwm-names = "backlight";
brightness-levels = <
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40 41 42 43 44
45 46 47 48 49 50 51 52 53 54 55 56 57 58
59 60 61 62 63 64 65 66 67 68 69 70 71 72
73 74 75 76 77 78 79 80 81 82 83 84 85 86
87 88 89 90 91 92 93 94 95 96 97 98 99 100
>;
default-brightness-level = <50>;
enable-gpios = <&gpio TEGRA_GPIO(V, 1) GPIO_ACTIVE_HIGH>;
power-supply = <&max77620_sd3>;
};
/* Fixed regulators */
battery_reg: vdd-ac-bat {
compatible = "regulator-fixed";
status = "okay";
regulator-name = "vdd-ac-bat";
regulator-min-microvolt = <4800000>;
regulator-max-microvolt = <4800000>;
regulator-always-on;
regulator-boot-on;
};
vdd_3v3: vdd-3v3 {
compatible = "regulator-fixed";
regulator-name = "vdd-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
gpio = <&max77620 3 0>;
enable-active-high;
regulator-enable-ramp-delay = <160>;
regulator-disable-ramp-delay = <10000>;
};
max77620_gpio7: avdd-dsi-csi-1v2 {
compatible = "regulator-fixed";
regulator-name = "max77620-gpio7";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
regulator-boot-on; /* Must be set for seamless display */
gpio = <&max77620 7 0>;
enable-active-high;
regulator-enable-ramp-delay = <240>;
regulator-disable-ramp-delay = <11340>;
vin-supply = <&max77620_ldo0>;
};
lcd_bl_en: lcd-bl-en {
compatible = "regulator-fixed";
regulator-name = "lcd-bl-en";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
gpio = <&gpio TEGRA_GPIO(V, 1) 0>;
regulator-boot-on; /* Must be set for seamless display */
enable-active-high;
regulator-always-on;
regulator-state-mem {
regulator-off-in-suspend;
};
};
en_vdd_sd: en-vdd-sd {
compatible = "regulator-fixed";
regulator-name = "en-vdd-sd";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio TEGRA_GPIO(E, 4) 0>;
enable-active-high;
regulator-enable-ramp-delay = <472>;
regulator-disable-ramp-delay = <4880>;
vin-supply = <&vdd_3v3>;
};
/* LCD Power Enable +5V. Rohm BD8316GWL. */
v_pavdd_5v0: v-pavdd-5v0 {
compatible = "regulator-fixed";
regulator-name = "v_pavdd_5v0";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
gpio = <&gpio TEGRA_GPIO(I, 0) 0>;
enable-active-high;
regulator-boot-on; /* Must be set for seamless display */
regulator-enable-ramp-delay = <232>;
};
/* LCD Power Enable -5V. Rohm BD8316GWL. */
v_navdd_5v0: v-navdd-5v0 {
compatible = "regulator-fixed";
regulator-name = "v_navdd_5v0";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
gpio = <&gpio TEGRA_GPIO(I, 1) 0>;
enable-active-high;
regulator-boot-on; /* Must be set for seamless display */
regulator-enable-ramp-delay = <232>;
};
/* Joycon/Fan power (battery). Maxim MAX8969EWL53+. */
v_vdd50_a: v-vdd50-a {
compatible = "regulator-fixed";
regulator-name = "v_vdd50_a";
regulator-min-microvolt = <5300000>;
regulator-max-microvolt = <5300000>;
gpio = <&gpio TEGRA_GPIO(A, 5) 0>;
enable-active-high;
};
/* Joycon/Fan power (usb). DC0 on Odin. Maxim MAX8969EWL53+ on Modin. */
v_vdd50_b: v-vdd50-b {
compatible = "regulator-fixed";
regulator-name = "v_vdd50_b";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpio = <&gpio TEGRA_GPIO(CC, 4) 0>;
enable-active-high;
};
v_vdd5v3: v-vdd5v3 {
compatible = "regulator-fixed";
status = "okay"; /* Only enabled on Modin/Vali/Fric */
regulator-name = "v_vdd5v3";
regulator-min-microvolt = <5300000>;
regulator-max-microvolt = <5300000>;
gpio = <&gpio TEGRA_GPIO(X, 3) 0>;
enable-active-high;
regulator-enable-ramp-delay = <10000>;
};
vdd3v3_gc: vdd3v3-gc {
compatible = "regulator-fixed";
regulator-name = "vdd3v3_gc";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio TEGRA_GPIO(E, 5) 0>;
enable-active-high;
regulator-enable-ramp-delay = <472>;
regulator-disable-ramp-delay = <4880>;
vin-supply = <&vdd_3v3>;
};
/* TI TCR2EE48 4.8V */
en_vdd_jcl_chgr: left-joycon-charger {
compatible = "regulator-fixed";
status = "okay";
regulator-name = "left-joycon-charger";
regulator-min-microvolt = <4800000>;
regulator-max-microvolt = <4800000>;
gpio = <&gpio TEGRA_GPIO(CC, 3) GPIO_ACTIVE_HIGH>;
enable-active-high;
vin-supply = <&v_vdd50_a>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
/* TI TCR2EE48 4.8V */
en_vdd_jcr_chgr: right-joycon-charger {
compatible = "regulator-fixed";
status = "okay";
regulator-name = "right-joycon-charger";
regulator-min-microvolt = <4800000>;
regulator-max-microvolt = <4800000>;
gpio = <&gpio TEGRA_GPIO(K, 3) GPIO_ACTIVE_HIGH>;
enable-active-high;
vin-supply = <&v_vdd50_a>;
regulator-state-mem {
regulator-off-in-suspend;
};
};
/* SDMMC4 for EMMC */
mmc@700b0600 {
status = "disabled";
bus-width = <8>;
max-frequency = <200000000>;
cap-mmc-highspeed;
mmc-ddr-1_8v;
mmc-hs200-1_8v;
mmc-hs400-1_8v;
non-removable;
vqmmc-supply = <&max77620_sd3>;
vmmc-supply = <&vdd_3v3>;
};
/* SDMMC3 Not Used */
mmc@700b0400 {
status = "disabled";
vqmmc-supply = <&max77620_sd3>;
vmmc-supply = <&vdd_3v3>;
};
/* SDMMC2 for Gamecard */
mmc@700b0200 {
status = "disabled";
bus-width = <8>;
max-frequency = <200000000>;
mmc-ddr-1_8v;
mmc-hs400-1_8v;
mmc-hs200-1_8v;
cap-mmc-highspeed;
cap-sd-highspeed;
non-removable;
vqmmc-supply = <&max77620_sd3>;
vmmc-supply = <&vdd_3v3>;
};
/* SDMMC1 for SD Card */
mmc@700b0000 {
status = "okay";
bus-width = <4>;
max-frequency = <200000000>;
cap-sd-highspeed;
sd-uhs-sdr12;
sd-uhs-sdr50;
sd-uhs-sdr104;
cd-gpios = <&gpio TEGRA_GPIO(Z, 1) GPIO_ACTIVE_LOW>;
vmmc-supply = <&en_vdd_sd>;
vqmmc-supply = <&max77620_ldo2>;
};
hda@70030000 {
status = "okay";
};
/* i2c1 @ 100000hz
1c: realtek 5639
18: rohm usb-pd
4c: ti temperature sensor
6b: ti charger
36: maxim fuel gauge
*/
i2c@7000c000 {
status = "okay";
clock-frequency = <100000>;
bq24193@6b {
compatible = "ti,bq24193";
reg = <0x6b>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(Z, 0) IRQ_TYPE_LEVEL_LOW>;
monitored-battery = <&fuel_gauge>;
#extcon-cells = <1>;
omit-battery-class;
battery_charger: charger {
regulator-name = "batt_regulator";
regulator-max-microamp = <4500000>;
};
usb0_vbus: usb-otg-vbus {
regulator-name = "vbus_regulator";
};
};
fuel_gauge: fuel-gauge@36 {
compatible = "maxim,max17050";
status = "okay";
reg = <0x36>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(Y, 0) IRQ_TYPE_NONE>;
/* Actual is 5000 but driver does not account for CGAIN */
/* And does not take into account Rsense and CGAIN for capacity */
maxim,rsns-microohm = <10000>;
maxim,over-heat-temp = <600>;
maxim,dead-volt = <3000>;
maxim,over-volt = <4208>; /* Actual: 4258 mV */
#thermal-sensor-cells = <0>;
};
rt5639: audio-codec@1c {
status = "okay";
compatible = "realtek,rt5639";
reg = <0x1c>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(BB, 4) IRQ_TYPE_EDGE_RISING>;
realtek,ldo1-en-gpios = <&gpio TEGRA_GPIO(Z, 4) GPIO_ACTIVE_HIGH>;
realtek,over-current-threshold-microamp = <600>;
realtek,over-current-scale-factor = <2>;
realtek,jack-detect-is-jd1;
};
/* PD Chip */
bm92t: bm92t@18 {
compatible = "rohm,bm92t";
reg = <0x18>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(K, 4) IRQ_TYPE_EDGE_RISING>;
#extcon-cells = <1>;
pd_bat_chg-supply = <&battery_charger>;
vbus-source-supply = <&v_vdd5v3>;
vbus-supply = <&usb0_vbus>;
rohm,dp-lanes = <2>;
rohm,dp-signal-toggle-on-resume;
/* Absolute max is 2.4A, constrained by BQ24193 ILIM */
rohm,pd-5v-current-limit-ma = <2000>;
rohm,pd-9v-current-limit-ma = <2000>;
rohm,pd-12v-current-limit-ma = <1500>;
rohm,pd-15v-current-limit-ma = <1200>;
port {
usb_con_ep: endpoint {
remote-endpoint = <&usb_port_0>;
};
};
};
tmp451: temperature-sensor@4c {
compatible = "ti,tmp451";
status = "okay";
reg = <0x4c>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(X, 4) IRQ_TYPE_EDGE_FALLING>;
vcc-supply = <&battery_reg>;
#thermal-sensor-cells = <1>;
};
};
/* i2c3 @ 400000hz
49: stm touchscreen controller
*/
i2c@7000c500 {
status = "okay";
clock-frequency = <400000>;
touchscreen@49 {
compatible = "stm,ftm4_fts";
status = "okay";
reg = <0x49>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(X, 1) IRQ_TYPE_LEVEL_LOW>;
vdd-tp-2v9-supply = <&max77620_ldo6>;
stm,vio-gpio = <&gpio TEGRA_GPIO(J, 7) GPIO_ACTIVE_HIGH>;
stm,irq_type = <0x2000>; /* IRQF_ONESHOT */
stm,regulator_avdd = "vdd-tp-2v9";
stm,max_coords = <1280 720>;
stm,max-real-coords = <1264 704>;
stm,edge-offset = <15 15>;
stm,delayed-open;
stm,delayed-open-time = <0>;
};
};
/* i2c2 @ 400000hz
29: rohm ambient light sensor
*/
i2c@7000c400 {
status = "okay";
clock-frequency = <400000>;
bh1730fvc: bh1730fvc@29 {
status = "disabled";
compatible = "rohm,bh1730fvc";
reg = <0x29>;
als-vid-supply = <&max77620_ldo6>;
als-vdd-supply = <&vdd_3v3>;
rohm,integration-cycle = <38>;
rohm,lux-multiplier = <3600>;
rohm,opt-win-coeff = < 500 5002 7502
754 2250 2000
1029 1999 1667
1373 884 583
1879 309 165>;
rohm,gain-coeff = < 2500 0
1600 7800
10 50000
0 1000>;
};
};
spi@7000da00 {
status = "disabled"; /* Disabled for Vali */
num-cs = <1>;
#address-cells = <1>;
#size-cells = <0>;
// spi-cs-setup-delay-ns = <2>;
// spi-cs-hold-delay-ns = <2>;
// spi-cs-inactive-delay-ns = <2>;
/*
* Model WHOAMI:
* LSM6DS3H 0x69, LSM6DSE 0x6A, LSM6DSO 0x6C, ICM40607 0x38.
*/
gyroscope: imu@0 {
reg = <0>;
status = "disabled";
compatible = "st,lsm6ds3h";
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <8000000>;
/* SPI Mode-3 */
spi-cpha;
spi-cpol;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(X, 2) IRQ_TYPE_LEVEL_HIGH>;
vdd-supply = <&battery_reg>;
vddio-supply = <&battery_reg>;
st,drdy-int-pin = <2>;
nvidia,rx-clk-tap-delay = <0x1f>;
nvidia,tx-clk-tap-delay = <0x0>;
mount-matrix =
"1", "0", "0", /* X: x */
"0", "1", "0", /* Y: y */
"0", "0", "1"; /* Z: z */
};
};
gpio-keys {
compatible = "gpio-keys";
key-volume-down {
label = "Volume Down";
gpios = <&gpio TEGRA_GPIO(X, 7) GPIO_ACTIVE_LOW>;
linux,input-type = <EV_KEY>;
linux,code = <KEY_VOLUMEDOWN>;
debounce-interval = <10>;
};
key-volume-up {
label = "Volume Up";
gpios = <&gpio TEGRA_GPIO(X, 6) GPIO_ACTIVE_LOW>;
linux,input-type = <EV_KEY>;
linux,code = <KEY_VOLUMEUP>;
debounce-interval = <10>;
};
};
gpio@6000d000 {
boot-default-output-high {
gpio-hog;
output-high;
gpios = <TEGRA_GPIO(H, 1) GPIO_ACTIVE_HIGH>; /* Wi-Fi Reset. Powered on by default. */
line-name = "boot-default-output-high";
};
};
pwm@7000a000 {
status = "okay";
#pwm-cells = <2>;
};
fan: pwm-fan {
compatible = "pwm-fan";
cooling-min-state = <0>;
cooling-max-state = <5>;
#cooling-cells = <2>;
pwms = <&pwm 1 33333>;
fan-supply = <&v_vdd50_a>;
enable-gpios = <&gpio TEGRA_GPIO(A, 5) GPIO_ACTIVE_HIGH>;
/* cooling level (0, 1, 2, 3, 4, 5) - pwm inverted */
cooling-levels = <255 204 153 102 51 0>;
};
sound {
status = "okay";
compatible = "nvidia,tegra-audio-rt5640-tx1",
"nvidia,tegra-audio-rt5640";
nvidia,model = "Nintendo Switch";
nvidia,audio-codec = <&rt5639>;
nvidia,i2s-controller = <&tegra_i2s1>;
// nvidia,hp-det-gpios = <&gpio 143 0>;
clocks = <&tegra_car TEGRA210_CLK_PLL_A>,
<&tegra_car TEGRA210_CLK_PLL_A_OUT0>,
<&tegra_car TEGRA210_CLK_CLK_M>;
clock-names = "pll_a", "plla_out0", "mclk";
assigned-clocks = <&tegra_car TEGRA210_CLK_PLL_A>,
<&tegra_car TEGRA210_CLK_PLL_A_OUT0>,
<&tegra_car TEGRA210_CLK_D_AUDIO>,
<&tegra_car TEGRA210_CLK_EXTERN1>;
assigned-clock-rates = <368640000>, <36864000>,
<36864000>, <12288000>;
nvidia,audio-routing =
"Headphones", "HPOL",
"Headphones", "HPOR",
"Speakers", "SPORP",
"Speakers", "SPORN",
"Speakers", "SPOLP",
"Speakers", "SPOLN",
"IN1P", "Mic Jack",
"IN1P", "Mic Jack";
};
thermal-zones {
cpu {
polling-delay-passive = <250>;
polling-delay = <500>;
status = "okay";
/* Based on Console profile SoC: 95% (100% at Handheld) */
coefficients = <95 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0>;
thermal-sensors = <&tmp451 1>;
trips {
cpu_critical: cpu-critical {
temperature = <102500>;
hysteresis = <0>;
type = "critical";
};
cpu_heavy: cpu-heavy {
temperature = <98500>;
hysteresis = <1000>;
type = "hot";
};
cpu_throttle: cpu-throttle {
temperature = <89000>;
hysteresis = <2000>;
type = "passive";
};
cpu_cap_trip1: cpu-cap-trip1 {
temperature = <58000>;
hysteresis = <2000>;
type = "active";
};
cpu_floor_trip1: cpu-floor-trip1 {
temperature = <53000>;
hysteresis = <2000>;
type = "active";
};
cpu_cap_trip0: cpu-cap-trip0 {
temperature = <48000>;
hysteresis = <2000>;
type = "active";
};
cpu_floor_trip0: cpu-floor-trip0 {
temperature = <43000>;
hysteresis = <2000>;
type = "active";
};
};
cooling-maps {
map0 {
cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
trip = <&cpu_critical>;
};
map1 {
cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
trip = <&cpu_heavy>;
};
map2 {
cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
trip = <&cpu_throttle>;
};
map3 {
cooling-device = <&fan 1 1>;
trip = <&cpu_cap_trip1>;
};
map4 {
cooling-device = <&fan 2 2>;
trip = <&cpu_floor_trip1>;
};
map5 {
cooling-device = <&fan 3 3>;
trip = <&cpu_cap_trip0>;
};
map6 {
cooling-device = <&fan 4 4>;
trip = <&cpu_floor_trip0>;
};
};
};
gpu {
polling-delay-passive = <250>;
polling-delay = <500>;
status = "okay";
thermal-sensors = <&tmp451 1>;
trips {
gpu_critical: gpu-critical {
temperature = <103000>;
hysteresis = <0>;
type = "critical";
};
gpu_heavy: gpu-heavy {
temperature = <100000>;
hysteresis = <1000>;
type = "hot";
};
gpu_throttle: gpu-throttle {
temperature = <90500>;
hysteresis = <0>;
type = "passive";
};
};
cooling-maps {
map0 {
trip = <&gpu_critical>;
cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
};
map1 {
trip = <&gpu_heavy>;
cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
};
map2 {
trip = <&gpu_throttle>;
cooling-device = <&fan THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
};
};
};
pmic {
status = "okay";
polling-delay-passive = <250>;
polling-delay = <500>;
thermal-sensors = <&max77620>;
trips {
pmic_die_warn_temp_thresh: hot-die {
temperature = <120000>;
type = "hot";
hysteresis = <0>;
};
pmic_die_cirt_temp_thresh: critical-die {
temperature = <140000>;
type = "critical";
hysteresis = <0>;
};
};
cooling-maps {
map0 {
trip = <&pmic_die_warn_temp_thresh>;
cooling-device = <&throttle_heavy 1 1>;
contribution = <100>;
};
};
};
cpu_vreg {
status = "okay";
polling-delay-passive = <250>;
polling-delay = <500>;
thermal-sensors = <&cpu_max_reg>;
trips {
cpu_vreg_die_warn_temp_thresh: hot-die {
temperature = <120000>;
type = "hot";
hysteresis = <0>;
};
};
cooling-maps {
map0 {
trip = <&cpu_vreg_die_warn_temp_thresh>;
cooling-device = <&throttle_heavy 1 1>;
contribution = <100>;
};
};
};
gpu_vreg {
status = "okay";
polling-delay-passive = <250>;
polling-delay = <500>;
thermal-sensors = <&gpu_max_reg>;
trips {
gpu_vreg_die_warn_temp_thresh: hot-die {
temperature = <120000>;
type = "hot";
hysteresis = <0>;
};
};
cooling-maps {
/*
* There are currently no cooling maps,
* because there are no cooling devices.
*/
};
};
};
};
File diff suppressed because it is too large. Load diff
@@ -1,9 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
#include "tegra210b01-nx.dtsi"
/ {
model = "Nintendo Switch (OLED model)";
compatible = "nvidia,fric", "nintendo,aula", "nintendo,nx", "nvidia,tegra210b01";
};
File diff suppressed because it is too large. Load diff
@@ -1,10 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
#include "tegra210b01-nx.dtsi"
/ {
model = "Nintendo Switch (2019)";
compatible = "nvidia,modin", "nvidia,odin", "nintendo,iowa", "nintendo,nx", "nvidia,tegra210b01";
};
@@ -48,6 +48,8 @@
};
pinmux@700008d4 {
compatible = "nvidia,tegra210b01-pinmux";
pinmux-sdmmc1-1v8-drv {
sdmmc1 {
nvidia,pull-down-strength = <0x8>;
+326 -15
View File
@@ -31,6 +31,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
@@ -193,6 +194,37 @@
*/
#define REF_CLK_CYC_PER_DVCO_SAMPLE 4
/*
* DFLL_OUTPUT_RAMP_DELAY: after the DFLL's I2C PMIC output starts
* requesting the minimum TUNE_HIGH voltage, the minimum number of
* microseconds to wait for the PMIC's voltage output to finish
* slewing
*/
#define DFLL_OUTPUT_RAMP_DELAY 100
/*
* DFLL_TUNE_HIGH_DELAY: number of microseconds to wait between tests
* to see if the high voltage has been reached yet, during a
* transition from the low-voltage range to the high-voltage range
*/
#define DFLL_TUNE_HIGH_DELAY 2000
/*
* DFLL_TUNE_HIGH_MARGIN_STEPS: attempt to initially program the DFLL
* voltage target to a (DFLL_TUNE_HIGH_MARGIN_STEPS * 10 millivolt)
* margin above the high voltage floor, in closed-loop mode in the
* high-voltage range
*/
#define DFLL_TUNE_HIGH_MARGIN_STEPS 2
/*
* I2C_OUTPUT_ACTIVE_TEST_US: mandatory minimum interval (in
* microseconds) between testing whether the I2C controller is
* currently sending a voltage-set command. Some comments list this
* as being a worst-case margin for "disable propagation."
*/
#define I2C_OUTPUT_ACTIVE_TEST_US 2
/*
* REF_CLOCK_RATE: the DFLL reference clock rate currently supported by this
* driver, in Hz
@@ -224,6 +256,9 @@ enum dfll_ctrl_mode {
* enum dfll_tune_range - voltage range that the driver believes it's in
* @DFLL_TUNE_UNINITIALIZED: DFLL tuning not yet programmed
* @DFLL_TUNE_LOW: DFLL in the low-voltage range (or open-loop mode)
* @DFLL_TUNE_WAIT_DFLL: waiting for DFLL voltage output to reach high
* @DFLL_TUNE_WAIT_PMIC: waiting for PMIC to react to DFLL output
* @DFLL_TUNE_HIGH: DFLL in the high-voltage range
*
* Some DFLL tuning parameters may need to change depending on the
* DVCO's voltage; these states represent the ranges that the driver
@@ -232,7 +267,10 @@ enum dfll_ctrl_mode {
*/
enum dfll_tune_range {
DFLL_TUNE_UNINITIALIZED = 0,
DFLL_TUNE_LOW = 1,
DFLL_TUNE_LOW,
DFLL_TUNE_WAIT_DFLL,
DFLL_TUNE_WAIT_PMIC,
DFLL_TUNE_HIGH,
};
@@ -285,6 +323,10 @@ struct tegra_dfll {
struct dfll_rate_req last_req;
unsigned long last_unrounded_rate;
struct hrtimer tune_timer;
ktime_t tune_delay;
ktime_t tune_ramp_delay;
/* Parameters from DT */
u32 droop_ctrl;
u32 sample_rate;
@@ -305,6 +347,11 @@ struct tegra_dfll {
int lut_size;
u8 lut_bottom, lut_min, lut_max, lut_safe;
/* tuning parameters */
u8 tune_high_out_start;
u8 tune_high_out_min;
u8 tune_out_last;
/* PWM interface */
enum tegra_dfll_pmu_if pmu_if;
unsigned long pwm_rate;
@@ -312,6 +359,9 @@ struct tegra_dfll {
struct pinctrl_state *pwm_enable_state;
struct pinctrl_state *pwm_disable_state;
u32 reg_init_uV;
/* spinlock protecting register accesses */
spinlock_t lock;
};
#define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw)
@@ -324,6 +374,8 @@ static const char * const mode_name[] = {
[DFLL_CLOSED_LOOP] = "closed_loop",
};
static void dfll_load_i2c_lut(struct tegra_dfll *td);
/*
* Register accessors
*/
@@ -361,6 +413,36 @@ static inline void dfll_i2c_wmb(struct tegra_dfll *td)
dfll_i2c_readl(td, DFLL_I2C_CFG);
}
/**
* is_output_i2c_req_pending - is an I2C voltage-set command in progress?
* @pdev: DFLL instance
*
* Returns 1 if an I2C request is in progress, or 0 if not. The DFLL
* IP block requires two back-to-back reads of the I2C_REQ_PENDING
* field to return 0 before the software can be sure that no I2C
* request is currently pending. Also, a minimum time interval
* between DFLL_I2C_STS reads is required by the IP block.
*/
static int is_output_i2c_req_pending(struct tegra_dfll *td)
{
u32 sts;
if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
return 0;
sts = dfll_i2c_readl(td, DFLL_I2C_STS);
if (sts & DFLL_I2C_STS_I2C_REQ_PENDING)
return 1;
udelay(I2C_OUTPUT_ACTIVE_TEST_US);
sts = dfll_i2c_readl(td, DFLL_I2C_STS);
if (sts & DFLL_I2C_STS_I2C_REQ_PENDING)
return 1;
return 0;
}
/**
* dfll_is_running - is the DFLL currently generating a clock?
* @td: DFLL instance
@@ -452,13 +534,139 @@ static void dfll_tune_low(struct tegra_dfll *td)
td->tune_range = DFLL_TUNE_LOW;
dfll_writel(td, td->soc->cvb->cpu_dfll_data.tune0_low, DFLL_TUNE0);
dfll_writel(td, td->soc->cvb->cpu_dfll_data.tune1, DFLL_TUNE1);
dfll_writel(td, td->soc->cvb->cpu_dfll_data.tune1_low, DFLL_TUNE1);
dfll_wmb(td);
if (td->soc->set_clock_trimmers_low)
td->soc->set_clock_trimmers_low();
}
/**
* dfll_tune_high - tune DFLL and CPU clock shaper for high voltages
* @td: DFLL instance
*
* Tune the DFLL oscillator parameters and the CPU clock shaper
* for the high-voltage range. The bottom end of the high-voltage
* range is represented by the index td->soc->tune_high_min_millivolts.
* Used in closed-loop mode. No return value.
*/
static void dfll_tune_high(struct tegra_dfll *td)
{
u32 tune0_high;
u32 tune1_high;
td->tune_range = DFLL_TUNE_HIGH;
tune0_high = td->soc->cvb->cpu_dfll_data.tune0_high;
if (!tune0_high)
tune0_high = td->soc->cvb->cpu_dfll_data.tune0_low;
dfll_writel(td, tune0_high, DFLL_TUNE0);
tune1_high = td->soc->cvb->cpu_dfll_data.tune1_high;
if (!tune1_high)
tune1_high = td->soc->cvb->cpu_dfll_data.tune1_low;
dfll_writel(td, tune1_high, DFLL_TUNE1);
dfll_wmb(td);
if (td->soc->set_clock_trimmers_high)
td->soc->set_clock_trimmers_high();
}
/**
* dfll_set_close_loop_config - prepare to switch to closed-loop mode
* @pdev: DFLL instance
* @req: requested output rate
*
* Prepare to switch the DFLL to closed-loop mode. This involves
* switching the DFLL's tuning voltage regime (if necessary), and
* rewriting the LUT to restrict the minimum and maximum voltages. No
* return value.
*/
static void dfll_set_close_loop_config(struct tegra_dfll *td,
struct dfll_rate_req *req)
{
bool sample_tune_out_last = false;
switch (td->tune_range) {
case DFLL_TUNE_LOW:
if (req->lut_index > td->tune_high_out_start) {
hrtimer_start(&td->tune_timer, td->tune_delay,
HRTIMER_MODE_REL);
}
sample_tune_out_last = true;
break;
case DFLL_TUNE_HIGH:
case DFLL_TUNE_WAIT_DFLL:
case DFLL_TUNE_WAIT_PMIC:
if (req->lut_index <= td->tune_high_out_start)
dfll_tune_low(td);
break;
default:
BUG();
}
td->lut_min = td->tune_range == DFLL_TUNE_LOW ?
0 : td->tune_high_out_min;
if (sample_tune_out_last && td->pmu_if == TEGRA_DFLL_PMU_I2C) {
u32 val;
val = dfll_i2c_readl(td, DFLL_I2C_STS);
td->tune_out_last = val >> DFLL_I2C_STS_I2C_LAST_SHIFT;
td->tune_out_last &= OUT_MASK;
}
}
/**
* dfll_tune_timer_cb - timer callback while tuning low to high
* @data: struct platform_device * of the DFLL instance
*
* Timer callback, used when switching from TUNE_LOW to TUNE_HIGH in
* closed-loop mode. Waits for DFLL I2C voltage command output to
* reach tune_high_out_min, then waits for the PMIC to react to the
* command. No return value.
*/
static enum hrtimer_restart dfll_tune_timer_cb(struct hrtimer *timer)
{
struct tegra_dfll *td;
u32 val, out_min, out_last;
bool use_ramp_delay;
td = container_of(timer, struct tegra_dfll, tune_timer);
if (td->tune_range == DFLL_TUNE_WAIT_DFLL) {
out_min = td->lut_min;
if (td->pmu_if == TEGRA_DFLL_PMU_I2C) {
val = dfll_i2c_readl(td, DFLL_I2C_STS);
out_last = val >> DFLL_I2C_STS_I2C_LAST_SHIFT;
out_last &= OUT_MASK;
} else {
out_last = out_min;
}
use_ramp_delay = !is_output_i2c_req_pending(td);
use_ramp_delay |= td->tune_out_last != out_last;
if (use_ramp_delay &&
(out_last >= td->tune_high_out_min) &&
(out_min >= td->tune_high_out_min)) {
td->tune_range = DFLL_TUNE_WAIT_PMIC;
hrtimer_start(&td->tune_timer, td->tune_ramp_delay,
HRTIMER_MODE_REL);
} else {
hrtimer_start(&td->tune_timer, td->tune_delay,
HRTIMER_MODE_REL);
}
} else if (td->tune_range == DFLL_TUNE_WAIT_PMIC) {
dfll_tune_high(td);
}
return HRTIMER_NORESTART;
}
/*
* Output clock scaler helpers
*/
@@ -554,7 +762,6 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable)
return 0;
}
/*
* DFLL-to-PWM controller interface
*/
@@ -679,10 +886,10 @@ static void dfll_load_i2c_lut(struct tegra_dfll *td)
u32 val;
for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
if (i < td->lut_min)
lut_index = td->lut_min;
else if (i > td->lut_max)
lut_index = td->lut_max;
if (i < td->lut_bottom)
lut_index = td->lut_bottom;
else if (i > td->lut_size - 1)
lut_index = td->lut_size - 1;
else
lut_index = i;
@@ -813,6 +1020,28 @@ static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate)
return -ENOENT;
}
/**
* find_mv_out_cap - find the out_map index with voltage >= @mv
* @td: DFLL instance
* @mv: millivolts
*
* Find the lut index with voltage greater than or equal to @mv,
* and return it. If all of the voltages in out_map are less than
* @mv, then return the lut index * corresponding to the highest
* possible voltage, even though it's less than @mv.
*/
static u8 find_mv_out_cap(struct tegra_dfll *td, int mv)
{
u8 i;
for (i = td->lut_bottom; i < td->lut_size; i++) {
if (td->lut_uv[i] >= mv * 1000)
return i;
}
return i - 1; /* maximum possible output */
}
/**
* dfll_calculate_rate_request - calculate DFLL parameters for a given rate
* @td: DFLL instance
@@ -913,6 +1142,7 @@ static int dfll_request_rate(struct tegra_dfll *td, unsigned long rate)
{
int ret;
struct dfll_rate_req req;
unsigned long flags;
if (td->mode == DFLL_UNINITIALIZED) {
dev_err(td->dev, "%s: Cannot set DFLL rate in %s mode\n",
@@ -927,8 +1157,12 @@ static int dfll_request_rate(struct tegra_dfll *td, unsigned long rate)
td->last_unrounded_rate = rate;
td->last_req = req;
if (td->mode == DFLL_CLOSED_LOOP)
if (td->mode == DFLL_CLOSED_LOOP) {
spin_lock_irqsave(&td->lock, flags);
dfll_set_close_loop_config(td, &td->last_req);
dfll_set_frequency_request(td, &td->last_req);
spin_unlock_irqrestore(&td->lock, flags);
}
return 0;
}
@@ -946,13 +1180,18 @@ static int dfll_request_rate(struct tegra_dfll *td, unsigned long rate)
*/
static int dfll_disable(struct tegra_dfll *td)
{
unsigned long flags;
if (td->mode != DFLL_OPEN_LOOP) {
dev_err(td->dev, "cannot disable DFLL in %s mode\n",
mode_name[td->mode]);
return -EINVAL;
}
spin_lock_irqsave(&td->lock, flags);
dfll_set_mode(td, DFLL_DISABLED);
spin_unlock_irqrestore(&td->lock, flags);
pm_runtime_put_sync(td->dev);
return 0;
@@ -1016,6 +1255,7 @@ static void dfll_set_open_loop_config(struct tegra_dfll *td)
static int dfll_lock(struct tegra_dfll *td)
{
struct dfll_rate_req *req = &td->last_req;
unsigned long flags;
switch (td->mode) {
case DFLL_CLOSED_LOOP:
@@ -1033,9 +1273,12 @@ static int dfll_lock(struct tegra_dfll *td)
else
dfll_i2c_set_output_enabled(td, true);
spin_lock_irqsave(&td->lock, flags);
dfll_set_mode(td, DFLL_CLOSED_LOOP);
dfll_set_close_loop_config(td, req);
dfll_set_frequency_request(td, req);
dfll_set_force_output_enabled(td, false);
spin_unlock_irqrestore(&td->lock, flags);
return 0;
default:
@@ -1055,14 +1298,19 @@ static int dfll_lock(struct tegra_dfll *td)
*/
static int dfll_unlock(struct tegra_dfll *td)
{
unsigned long flags;
switch (td->mode) {
case DFLL_CLOSED_LOOP:
spin_lock_irqsave(&td->lock, flags);
dfll_set_open_loop_config(td);
dfll_set_mode(td, DFLL_OPEN_LOOP);
if (td->pmu_if == TEGRA_DFLL_PMU_PWM)
dfll_pwm_set_output_enabled(td, false);
else
dfll_i2c_set_output_enabled(td, false);
spin_unlock_irqrestore(&td->lock, flags);
return 0;
case DFLL_OPEN_LOOP:
@@ -1325,8 +1573,10 @@ static int attr_registers_show(struct seq_file *s, void *data)
{
u32 val, offs;
struct tegra_dfll *td = s->private;
unsigned long flags;
seq_puts(s, "CONTROL REGISTERS:\n");
spin_lock_irqsave(&td->lock, flags);
for (offs = 0; offs <= DFLL_MONITOR_DATA; offs += 4) {
if (offs == DFLL_OUTPUT_CFG)
val = dfll_i2c_readl(td, offs);
@@ -1355,6 +1605,8 @@ static int attr_registers_show(struct seq_file *s, void *data)
__raw_readl(td->lut_base + offs));
}
spin_unlock_irqrestore(&td->lock, flags);
return 0;
}
@@ -1445,6 +1697,53 @@ static int dfll_init_clks(struct tegra_dfll *td)
return 0;
}
/**
* dfll_init_tuning_thresholds - set up the high voltage range, if possible
* @td: DFLL instance
*
* Determine whether the DFLL tuning parameters need to be
* reprogrammed when the DFLL voltage reaches a certain minimum
* threshold. No return value.
*/
static void dfll_init_tuning_thresholds(struct tegra_dfll *td)
{
u8 out_min, out_start, max_voltage_index;
max_voltage_index = td->lut_size - 1;
/*
* Convert high tuning voltage threshold into output LUT
* index, and add necessary margin. If voltage threshold is
* outside operating range set it at maximum output level to
* effectively disable tuning parameters adjustment.
*/
td->tune_high_out_min = max_voltage_index;
td->tune_high_out_start = max_voltage_index;
if (td->soc->tune_high_min_millivolts < td->soc->min_millivolts)
return; /* no difference between low & high voltage range */
out_min = find_mv_out_cap(td, td->soc->tune_high_min_millivolts);
if ((out_min + 2) > max_voltage_index)
return;
if (td->soc->cvb->cpu_dfll_data.tune_high_margin_millivolts) {
unsigned int mv;
mv = td->soc->tune_high_min_millivolts +
td->soc->cvb->cpu_dfll_data.tune_high_margin_millivolts;
if (mv * 1000 > td->lut_uv[max_voltage_index])
return;
out_start = max((u8)(out_min + 1), find_mv_out_cap(td, mv));
} else {
out_start = out_min + DFLL_TUNE_HIGH_MARGIN_STEPS;
}
if (out_start > max_voltage_index)
return;
td->tune_high_out_min = out_min;
td->tune_high_out_start = out_start;
}
/**
* dfll_init - Prepare the DFLL IP block for use
* @td: DFLL instance
@@ -1501,6 +1800,8 @@ static int dfll_init(struct tegra_dfll *td)
dfll_init_out_if(td);
dfll_init_tuning_thresholds(td);
pm_runtime_put_sync(td->dev);
return 0;
@@ -1647,7 +1948,7 @@ static int dfll_build_pwm_lut(struct tegra_dfll *td, unsigned long v_max)
int i;
unsigned long rate, reg_volt;
u8 lut_bottom = MAX_DFLL_VOLTAGES;
int v_min = td->soc->cvb->min_millivolts * 1000;
int v_min = td->soc->min_millivolts * 1000;
for (i = 0; i < MAX_DFLL_VOLTAGES; i++) {
reg_volt = td->lut_uv[i];
@@ -1667,7 +1968,7 @@ static int dfll_build_pwm_lut(struct tegra_dfll *td, unsigned long v_max)
if ((lut_bottom == MAX_DFLL_VOLTAGES) ||
(lut_bottom + 1 >= td->lut_size)) {
dev_err(td->dev, "no voltage above DFLL minimum %d mV\n",
td->soc->cvb->min_millivolts);
td->soc->min_millivolts);
return -EINVAL;
}
td->lut_bottom = lut_bottom;
@@ -1676,7 +1977,7 @@ static int dfll_build_pwm_lut(struct tegra_dfll *td, unsigned long v_max)
rate = get_dvco_rate_below(td, td->lut_bottom);
if (!rate) {
dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n",
td->soc->cvb->min_millivolts);
td->soc->min_millivolts);
return -EINVAL;
}
td->dvco_rate_min = rate;
@@ -1699,17 +2000,20 @@ static int dfll_build_pwm_lut(struct tegra_dfll *td, unsigned long v_max)
*/
static int dfll_build_i2c_lut(struct tegra_dfll *td, unsigned long v_max)
{
unsigned long rate, v, v_opp;
unsigned long rate, v, v_opp, v_min_align;
int ret = -EINVAL;
int j, selector, lut;
v = td->soc->cvb->min_millivolts * 1000;
v = td->soc->min_millivolts * 1000;
lut = find_vdd_map_entry_exact(td, v);
if (lut < 0)
goto out;
td->lut[0] = lut;
td->lut_bottom = 0;
v_min_align = DIV_ROUND_UP(td->soc->min_millivolts * 1000,
td->soc->alignment.step_uv) * td->soc->alignment.step_uv;
for (j = 1, rate = 0; ; rate++) {
struct dev_pm_opp *opp;
@@ -1718,7 +2022,7 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td, unsigned long v_max)
break;
v_opp = dev_pm_opp_get_voltage(opp);
if (v_opp <= td->soc->cvb->min_millivolts * 1000)
if (v_opp <= v_min_align)
td->dvco_rate_min = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);
@@ -1749,7 +2053,7 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td, unsigned long v_max)
if (!td->dvco_rate_min)
dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n",
td->soc->cvb->min_millivolts);
td->soc->min_millivolts);
else {
ret = 0;
for (j = 0; j < td->lut_size; j++)
@@ -2057,12 +2361,19 @@ int tegra_dfll_register(struct platform_device *pdev,
if (ret)
return ret;
spin_lock_init(&td->lock);
ret = dfll_register_clk(td);
if (ret) {
dev_err(&pdev->dev, "DFLL clk registration failed\n");
return ret;
}
/* Initialize tuning timer */
hrtimer_setup(&td->tune_timer, dfll_tune_timer_cb, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
td->tune_delay = ktime_set(0, DFLL_TUNE_HIGH_DELAY * 1000);
td->tune_ramp_delay = ktime_set(0, DFLL_OUTPUT_RAMP_DELAY * 1000);
dfll_debug_init(td);
return 0;
+2
View File
@@ -31,6 +31,8 @@ struct tegra_dfll_soc_data {
unsigned long max_freq;
const struct cvb_table *cvb;
struct rail_alignment alignment;
unsigned int min_millivolts;
unsigned int tune_high_min_millivolts;
void (*init_clock_trimmers)(void);
void (*set_clock_trimmers_high)(void);
+42 -29
View File
@@ -64,7 +64,7 @@ static const unsigned long tegra114_cpu_max_freq_table[] = {
.cpu_dfll_data = { \
.tune0_low = 0x00b0039d, \
.tune0_high = 0x00b0009d, \
.tune1 = 0x0000001f, \
.tune1_low = 0x0000001f, \
.tune_high_min_millivolts = 1050, \
}
@@ -100,7 +100,7 @@ static const struct cvb_table tegra114_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0x00b0019d,
.tune0_high = 0x00b0019d,
.tune1 = 0x0000001f,
.tune1_low = 0x0000001f,
.tune_high_min_millivolts = 1000,
}
},
@@ -167,7 +167,7 @@ static const struct cvb_table tegra124_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0x005020ff,
.tune0_high = 0x005040ff,
.tune1 = 0x00000060,
.tune1_low = 0x00000060,
}
},
};
@@ -335,7 +335,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
.tune_high_min_millivolts = 864,
}
},
@@ -348,7 +348,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
.tune_high_min_millivolts = 864,
}
},
@@ -361,7 +361,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
}
},
{
@@ -373,7 +373,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
}
},
{
@@ -385,7 +385,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
}
},
{
@@ -397,7 +397,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
}
},
{
@@ -409,7 +409,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
.tune_high_min_millivolts = 864,
}
},
@@ -422,7 +422,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
.tune_high_min_millivolts = 864,
}
},
@@ -434,7 +434,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
TEGRA210_CPU_CVB_TABLE,
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
}
},
{
@@ -445,7 +445,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
TEGRA210_CPU_CVB_TABLE,
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune1 = 0x25501d0,
.tune1_low = 0x25501d0,
}
},
{
@@ -457,7 +457,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
.tune_high_min_millivolts = 864,
}
},
@@ -470,7 +470,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x25501d0,
.tune1_low = 0x25501d0,
.tune_high_min_millivolts = 864,
}
},
@@ -482,7 +482,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
TEGRA210_CPU_CVB_TABLE_XA,
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune1 = 0x17711BD,
.tune1_low = 0x17711BD,
}
},
{
@@ -494,7 +494,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
.tune_high_min_millivolts = 864,
}
},
@@ -507,7 +507,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x25501d0,
.tune1_low = 0x25501d0,
.tune_high_min_millivolts = 864,
}
},
@@ -519,7 +519,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
TEGRA210_CPU_CVB_TABLE,
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
}
},
{
@@ -530,7 +530,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
TEGRA210_CPU_CVB_TABLE,
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune1 = 0x25501d0,
.tune1_low = 0x25501d0,
}
},
{
@@ -542,7 +542,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
.tune_high_min_millivolts = 864,
}
},
@@ -555,7 +555,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x25501d0,
.tune1_low = 0x25501d0,
.tune_high_min_millivolts = 864,
}
},
@@ -568,7 +568,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x20091d9,
.tune1_low = 0x20091d9,
.tune_high_min_millivolts = 864,
}
},
@@ -581,7 +581,7 @@ static struct cvb_table tegra210_cpu_cvb_tables[] = {
.cpu_dfll_data = {
.tune0_low = 0xffead0ff,
.tune0_high = 0xffead0ff,
.tune1 = 0x25501d0,
.tune1_low = 0x25501d0,
.tune_high_min_millivolts = 864,
}
},
@@ -680,10 +680,13 @@ static struct cvb_table tegra210b01_cpu_cvb_tables[] = {
.process_id = -1,
.max_millivolts = 1120,
CPUB01_CVB_TABLE,
.vmin_coefficients = { 620000, 0, 0 },
.cpu_dfll_data = {
.tune0_low = 0x0000ffcf,
.tune1 = 0x012207ff,
.tune1_low = 0x012207ff,
.tune1_high = 0x03FFF7FF,
.tune_high_min_millivolts = 850,
.tune_high_margin_millivolts = 38,
}
},
{
@@ -691,11 +694,13 @@ static struct cvb_table tegra210b01_cpu_cvb_tables[] = {
.process_id = 1,
.max_millivolts = 1120,
CPUB01_CVB_TABLE_SLT_B1,
.vmin_coefficients = { 600000, 0, 0 },
.cpu_dfll_data = {
.tune0_low = 0x0000ffa0,
.tune0_high = 0x0000ffff,
.tune1 = 0x021107ff,
.tune1_low = 0x021107ff,
.tune_high_min_millivolts = 850,
.tune_high_margin_millivolts = 38,
}
},
{
@@ -703,11 +708,13 @@ static struct cvb_table tegra210b01_cpu_cvb_tables[] = {
.process_id = 0,
.max_millivolts = 1120,
CPUB01_CVB_TABLE_SLT_B0,
.vmin_coefficients = { 600000, 0, 0 },
.cpu_dfll_data = {
.tune0_low = 0x0000ff90,
.tune0_high = 0x0000ffff,
.tune1 = 0x021107ff,
.tune1_low = 0x021107ff,
.tune_high_min_millivolts = 850,
.tune_high_margin_millivolts = 38,
}
},
{
@@ -715,10 +722,12 @@ static struct cvb_table tegra210b01_cpu_cvb_tables[] = {
.process_id = -1,
.max_millivolts = 1120,
CPUB01_CVB_TABLE,
.vmin_coefficients = { 620000, 0, 0 },
.cpu_dfll_data = {
.tune0_low = 0x0000ffcf,
.tune1 = 0x012207ff,
.tune1_low = 0x012207ff,
.tune_high_min_millivolts = 850,
.tune_high_margin_millivolts = 38,
}
},
};
@@ -845,7 +854,8 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables,
fcpu_data->cpu_cvb_tables_size,
&align, process_id, speedo_id,
speedo_value, soc->max_freq);
speedo_value, soc->max_freq,
&soc->min_millivolts);
soc->alignment = align;
if (IS_ERR(soc->cvb)) {
@@ -854,6 +864,9 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev)
return PTR_ERR(soc->cvb);
}
soc->tune_high_min_millivolts =
soc->cvb->cpu_dfll_data.tune_high_min_millivolts;
err = tegra_dfll_register(pdev, soc);
if (err < 0) {
tegra_cvb_remove_opp_table(soc->dev, soc->cvb, soc->max_freq);
+13 -4
View File
@@ -54,13 +54,18 @@ static int round_voltage(int mv, const struct rail_alignment *align, int up)
static int build_opp_table(struct device *dev, const struct cvb_table *table,
struct rail_alignment *align,
int speedo_value, unsigned long max_freq)
int speedo_value, unsigned long max_freq, int *vmin)
{
int i, ret, dfll_mv, min_mv, max_mv;
min_mv = round_voltage(table->min_millivolts, align, UP);
max_mv = round_voltage(table->max_millivolts, align, DOWN);
dfll_mv = get_cvb_voltage(
speedo_value, table->speedo_scale, &table->vmin_coefficients);
dfll_mv = round_cvb_voltage(dfll_mv, table->voltage_scale, align);
min_mv = max(min_mv, dfll_mv);
for (i = 0; i < MAX_DVFS_FREQS; i++) {
const struct cvb_table_freq_entry *entry = &table->entries[i];
@@ -78,6 +83,9 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
return ret;
}
if (vmin)
*vmin = min_mv;
return 0;
}
@@ -90,7 +98,8 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table,
* @process_id: process id of the HW module
* @speedo_id: speedo id of the HW module
* @speedo_value: speedo value of the HW module
* @max_freq: highest safe clock rate
* @max_rate: highest safe clock rate
* @vmin: final minimum voltage returned to the caller
*
* On Tegra, a CVB table encodes the relationship between operating voltage
* and safe maximal frequency for a given module (e.g. GPU or CPU). This
@@ -103,7 +112,7 @@ const struct cvb_table *
tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
size_t count, struct rail_alignment *align,
int process_id, int speedo_id, int speedo_value,
unsigned long max_freq)
unsigned long max_freq, int *vmin)
{
size_t i;
int ret;
@@ -118,7 +127,7 @@ tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables,
continue;
ret = build_opp_table(dev, table, align, speedo_value,
max_freq);
max_freq, vmin);
return ret ? ERR_PTR(ret) : table;
}
+5 -2
View File
@@ -31,8 +31,10 @@ struct cvb_table_freq_entry {
struct cvb_cpu_dfll_data {
u32 tune0_low;
u32 tune0_high;
u32 tune1;
u32 tune1_low;
u32 tune1_high;
unsigned int tune_high_min_millivolts;
unsigned int tune_high_margin_millivolts;
};
struct cvb_table {
@@ -46,13 +48,14 @@ struct cvb_table {
int voltage_scale;
struct cvb_table_freq_entry entries[MAX_DVFS_FREQS];
struct cvb_cpu_dfll_data cpu_dfll_data;
struct cvb_coefficients vmin_coefficients;
};
const struct cvb_table *
tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables,
size_t count, struct rail_alignment *align,
int process_id, int speedo_id, int speedo_value,
unsigned long max_freq);
unsigned long max_freq, int *vmin);
void tegra_cvb_remove_opp_table(struct device *dev,
const struct cvb_table *table,
unsigned long max_freq);
+4 -4
View File
@@ -285,9 +285,9 @@ static void gpio_irq_unmask(struct irq_data *d)
gpiochip_enable_irq(&chips->chip, hwirq);
status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
status &= IRQ_TYPE_EDGE_BOTH;
if (!status)
status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING;
status = IRQ_TYPE_EDGE_BOTH;
if (status & IRQ_TYPE_EDGE_FALLING)
writel_relaxed(mask, &g->set_falling);
@@ -297,7 +297,7 @@ static void gpio_irq_unmask(struct irq_data *d)
static int gpio_irq_type(struct irq_data *d, unsigned trigger)
{
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
if (trigger & ~IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
return 0;
@@ -400,7 +400,7 @@ static int gpio_irq_type_unbanked(struct irq_data *data, unsigned trigger)
mask = __gpio_mask(i);
if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
if (trigger & ~IRQ_TYPE_EDGE_BOTH)
return -EINVAL;
writel_relaxed(mask, (trigger & IRQ_TYPE_EDGE_FALLING)
+1 -1
View File
@@ -160,7 +160,7 @@ mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
rg->hlevel | rg->llevel) & mask)
return 0;
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
type = IRQ_TYPE_EDGE_BOTH;
}
rg->rising &= ~mask;
+2 -2
View File
@@ -1263,14 +1263,14 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
gc = irq_get_domain_generic_chip(mvchip->domain, 0);
gc->private = mvchip;
ct = &gc->chip_types[0];
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
ct->type = IRQ_TYPE_LEVEL_MASK;
ct->chip.irq_mask = mvebu_gpio_level_irq_mask;
ct->chip.irq_unmask = mvebu_gpio_level_irq_unmask;
ct->chip.irq_set_type = mvebu_gpio_irq_set_type;
ct->chip.name = mvchip->chip.label;
ct = &gc->chip_types[1];
ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
ct->type = IRQ_TYPE_EDGE_BOTH;
ct->chip.irq_ack = mvebu_gpio_irq_ack;
ct->chip.irq_mask = mvebu_gpio_edge_irq_mask;
ct->chip.irq_unmask = mvebu_gpio_edge_irq_unmask;
+2 -2
View File
@@ -198,7 +198,7 @@ static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
gc->private = port;
ct = &gc->chip_types[0];
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
ct->type = IRQ_TYPE_LEVEL_MASK;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
@@ -210,7 +210,7 @@ static int mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
ct->regs.disable = PINCTRL_PIN2IRQ(port) + MXS_CLR;
ct = &gc->chip_types[1];
ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
ct->type = IRQ_TYPE_EDGE_BOTH;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_disable_reg;
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
+4 -5
View File
@@ -432,8 +432,7 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
if (type & ~IRQ_TYPE_SENSE_MASK)
return -EINVAL;
if (!bank->regs->leveldetect0 &&
(type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
if (!bank->regs->leveldetect0 && (type & IRQ_TYPE_LEVEL_MASK))
return -EINVAL;
raw_spin_lock_irqsave(&bank->lock, flags);
@@ -450,9 +449,9 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type)
}
raw_spin_unlock_irqrestore(&bank->lock, flags);
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
else if (type & IRQ_TYPE_EDGE_BOTH)
/*
* Edge IRQs are already cleared/acked in irq_handler and
* not need to be masked, as result handle_edge_irq()
@@ -702,7 +701,7 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
* after enabing the interrupt to clear the wakeup status.
*/
if (bank->regs->leveldetect0 && bank->regs->wkup_en &&
trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
trigger & IRQ_TYPE_LEVEL_MASK)
omap_clear_gpio_irqstatus(bank, offset);
if (trigger)
+2 -3
View File
@@ -132,8 +132,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
if (offset < 0 || offset >= PL061_GPIO_NR)
return -EINVAL;
if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
(trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
if ((trigger & IRQ_TYPE_LEVEL_MASK) && (trigger & IRQ_TYPE_EDGE_BOTH))
{
dev_err(gc->parent,
"trying to configure line %d for both level and edge "
@@ -149,7 +148,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
gpiois = readb(pl061->base + GPIOIS);
gpioibe = readb(pl061->base + GPIOIBE);
if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
if (trigger & IRQ_TYPE_LEVEL_MASK) {
bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
/* Disable edge detection */
+1 -1
View File
@@ -406,7 +406,7 @@ static int pxa_gpio_irq_type(struct irq_data *d, unsigned int type)
if (__gpio_is_occupied(pchip, gpio))
return 0;
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
type = IRQ_TYPE_EDGE_BOTH;
}
gpdr = readl_relaxed(c->regbase + GPDR_OFFSET);
+2 -2
View File
@@ -169,9 +169,9 @@ static int rda_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type)
if (ret)
return ret;
if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
if (flow_type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(data, handle_level_irq);
else if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
else if (flow_type & IRQ_TYPE_EDGE_BOTH)
irq_set_handler_locked(data, handle_edge_irq);
return 0;
+1 -1
View File
@@ -134,7 +134,7 @@ static int sa1100_gpio_type(struct irq_data *d, unsigned int type)
if (type == IRQ_TYPE_PROBE) {
if ((sgc->irqrising | sgc->irqfalling) & mask)
return 0;
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
type = IRQ_TYPE_EDGE_BOTH;
}
if (type & IRQ_TYPE_EDGE_RISING)
+1 -1
View File
@@ -158,7 +158,7 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd,
sd->gc->private = sd;
ct = sd->gc->chip_types;
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
ct->type = IRQ_TYPE_LEVEL_MASK;
ct->regs.eoi = GPSTR;
ct->regs.mask = GPIO_INT;
ct->chip.irq_mask = irq_gc_mask_clr_bit;
+1 -1
View File
@@ -149,7 +149,7 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
int regoffset = offset / 8;
int mask = BIT(offset % 8);
if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH)
if (type & IRQ_TYPE_LEVEL_MASK)
return -EINVAL;
/* STMPE801 and STMPE 1600 don't have RE and FE registers */
+4 -19
View File
@@ -172,18 +172,11 @@ static int tegra_gpio_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
int ret;
tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0);
tegra_gpio_enable(tgi, offset);
ret = pinctrl_gpio_direction_input(chip, offset);
if (ret < 0)
dev_err(tgi->dev,
"Failed to set pinctrl input direction of GPIO %d: %d",
chip->base + offset, ret);
return ret;
return 0;
}
static int tegra_gpio_direction_output(struct gpio_chip *chip,
@@ -191,19 +184,12 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip,
int value)
{
struct tegra_gpio_info *tgi = gpiochip_get_data(chip);
int ret;
tegra_gpio_set(chip, offset, value);
tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1);
tegra_gpio_enable(tgi, offset);
ret = pinctrl_gpio_direction_output(chip, offset);
if (ret < 0)
dev_err(tgi->dev,
"Failed to set pinctrl output direction of GPIO %d: %d",
chip->base + offset, ret);
return ret;
return 0;
}
static int tegra_gpio_get_direction(struct gpio_chip *chip,
@@ -356,9 +342,9 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
return ret;
}
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
if (type & IRQ_TYPE_LEVEL_MASK)
irq_set_handler_locked(d, handle_level_irq);
else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
else if (type & IRQ_TYPE_EDGE_BOTH)
irq_set_handler_locked(d, handle_edge_irq);
if (d->parent_data)
@@ -722,7 +708,6 @@ static int tegra_gpio_probe(struct platform_device *pdev)
tgi->gc.direction_output = tegra_gpio_direction_output;
tgi->gc.set = tegra_gpio_set;
tgi->gc.get_direction = tegra_gpio_get_direction;
tgi->gc.base = 0;
tgi->gc.ngpio = tgi->bank_count * 32;
tgi->gc.parent = &pdev->dev;
+1 -1
View File
@@ -149,7 +149,7 @@ static int timbgpio_irq_type(struct irq_data *d, unsigned trigger)
if (ver > 2)
bflr = ioread32(tgpio->membase + TGPIO_BFLR);
if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
if (trigger & IRQ_TYPE_LEVEL_MASK) {
bflr &= ~(1 << offset);
flr &= ~(1 << offset);
if (trigger & IRQ_TYPE_LEVEL_HIGH)
+1 -1
View File
@@ -1088,7 +1088,7 @@ static int tegra_dsi_init(struct host1x_client *client)
dev_err(dsi->dev, "failed to initialize output: %d\n",
err);
dsi->output.encoder.possible_crtcs = 0x3;
tegra_output_find_possible_crtcs(&dsi->output, drm);
}
return 0;
+31
View File
@@ -240,6 +240,12 @@
#define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c
#define XUSB_PADCTL_UPHY_PLL_S0_CTL10 0x384
#define CFG_ADDR(x) (((x) & 0xff) << 16)
#define CFG_WDATA(x) (((x) & 0xffff) << 0)
#define CFG_RESET (1 << 27)
#define CFG_WS (1 << 24)
#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960
#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL2 0x964
@@ -407,6 +413,18 @@
#define UHSIC_STROBE_RPD_C BIT(16)
#define UHSIC_STROBE_RPD_D BIT(24)
struct init_data {
u8 cfg_addr;
u16 cfg_wdata;
};
static struct init_data tegra210b01_usb3_pll_g1_init_data[] = {
{.cfg_addr = 0x2, .cfg_wdata = 0x0000},
{.cfg_addr = 0x3, .cfg_wdata = 0x7051},
{.cfg_addr = 0x25, .cfg_wdata = 0x0130},
{.cfg_addr = 0x1E, .cfg_wdata = 0x0017},
};
struct tegra210_xusb_fuse_calibration {
u32 hs_curr_level[4];
u32 hs_term_range_adj;
@@ -508,6 +526,7 @@ static int tegra210_usb3_lane_map(struct tegra_xusb_lane *lane)
static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
bool is_t210b01 = padctl->soc == &tegra210b01_xusb_padctl_soc;
unsigned long timeout;
u32 value;
unsigned int i;
@@ -533,6 +552,18 @@ static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
if (err < 0)
goto disable;
if (is_t210b01) {
for (i = 0; i < ARRAY_SIZE(tegra210b01_usb3_pll_g1_init_data); i++) {
value = 0;
value |= CFG_ADDR(tegra210b01_usb3_pll_g1_init_data[i].cfg_addr);
value |= CFG_WDATA(tegra210b01_usb3_pll_g1_init_data[i].cfg_wdata);
value |= CFG_RESET;
value |= CFG_WS;
padctl_writel(padctl, value,
XUSB_PADCTL_UPHY_PLL_S0_CTL10);
}
}
value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
value &= ~(XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_MASK <<
XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT);
+35 -3
View File
@@ -7,12 +7,17 @@
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include "pinctrl-tegra.h"
struct tegra210_pinctrl_soc {
bool lpdr_support;
};
/*
* Most pins affected by the pinmux can also be GPIOs. Define these first.
* These must match how the GPIO driver names/numbers its pins.
@@ -1349,7 +1354,7 @@ static const char * const tegra210_functions[] = {
.parked_bitmask = prk_mask, \
}
static const struct tegra_pingroup tegra210_groups[] = {
static struct tegra_pingroup tegra210_groups[] = {
/* pg_name, f0, f1, f2, f3, r, hsm, drvtype, e_io_hv, lpdr, rdrv, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, slwf_b, slwf_w */
PINGROUP(sdmmc1_clk_pm0, SDMMC1, RSVD1, RSVD2, RSVD3, 0x3000, Y, Y, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1),
PINGROUP(sdmmc1_cmd_pm1, SDMMC1, SPI3, RSVD2, RSVD3, 0x3004, Y, Y, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1),
@@ -1558,12 +1563,39 @@ static const struct tegra_pinctrl_soc_data tegra210_pinctrl = {
static int tegra210_pinctrl_probe(struct platform_device *pdev)
{
const struct tegra210_pinctrl_soc *soc;
struct tegra_pingroup *g;
int i;
soc = of_device_get_match_data(&pdev->dev);
if (soc->lpdr_support) {
for (i = 0; i < tegra210_pinctrl.ngroups; ++i) {
g = &tegra210_groups[i];
if (g->mux_reg >= 0)
g->lpdr_bit = 8;
}
}
return tegra_pinctrl_probe(pdev, &tegra210_pinctrl);
}
static const struct tegra210_pinctrl_soc tegra210_pinctrl_soc_data = {
.lpdr_support = false,
};
static const struct tegra210_pinctrl_soc tegra210b01_pinctrl_soc_data = {
.lpdr_support = true,
};
static const struct of_device_id tegra210_pinctrl_of_match[] = {
{ .compatible = "nvidia,tegra210-pinmux", },
{ },
{
.compatible = "nvidia,tegra210-pinmux",
.data = &tegra210_pinctrl_soc_data,
}, {
.compatible = "nvidia,tegra210b01-pinmux",
.data = &tegra210b01_pinctrl_soc_data,
}, {
},
};
static struct platform_driver tegra210_pinctrl_driver = {
+3 -2
View File
@@ -122,8 +122,9 @@ static void tegra_fuse_print_sku_info(struct tegra_sku_info *tegra_sku_info)
tegra_revision_name[tegra_sku_info->revision],
tegra_sku_info->sku_id, tegra_sku_info->cpu_process_id,
tegra_sku_info->soc_process_id);
pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
tegra_sku_info->cpu_speedo_id, tegra_sku_info->soc_speedo_id);
pr_info("Tegra CPU Speedo ID %d, SoC Speedo ID %d, GPU Speedo ID %d\n",
tegra_sku_info->cpu_speedo_id, tegra_sku_info->soc_speedo_id,
tegra_sku_info->gpu_speedo_id);
}
static int tegra_fuse_add_lookups(struct tegra_fuse *fuse)
-1
View File
@@ -68,7 +68,6 @@ config TYPEC_BM92TXX
tristate "Rohm Semiconductor BM92TXX USB Type-C Support"
depends on I2C
depends on USB_ROLE_SWITCH
depends on DRM
help
Say Y or M here if your system has Rohm BM92TXX USB Type-C and
Power Delivery controller.
+281 -290
View File
@@ -28,13 +28,13 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/debugfs.h>
#include <linux/extcon-provider.h>
#include <linux/usb/role.h>
#include <linux/usb/typec.h>
#include <linux/usb/pd.h>
#include <linux/usb/pd_vdo.h>
#include <linux/regulator/consumer.h>
#include <linux/uaccess.h>
#include <drm/drm_connector.h>
#ifdef CONFIG_SWITCH
#include <linux/switch.h>
#endif
/* Registers */
@@ -258,6 +258,16 @@
#define STOP_HPD_CMD 0x3939 /* Stop handling HPD */
#define STOP_HPD_EXIT_DP_CMD 0x3B3B /* Stop handling HPD and exit DP Alt mode */
/* General defines */
#define PDO_TYPE_FIXED 0
#define PDO_TYPE_BATT 1
#define PDO_TYPE_VAR 2
#define PDO_INFO_DR_DATA (1 << 5)
#define PDO_INFO_USB_COMM (1 << 6)
#define PDO_INFO_EXT_POWER (1 << 7)
#define PDO_INFO_HP_CAP (1 << 8)
#define PDO_INFO_DR_POWER (1 << 9)
/* VDM/VDO */
#define VDM_CMD_RESERVED 0x00
@@ -276,6 +286,14 @@
#define VDM_UNSTRUCTURED 0x00
#define VDM_STRUCTURED 0x80
/* VDM Discover ID */
#define VDO_ID_TYPE_NONE 0
#define VDO_ID_TYPE_PD_HUB 1
#define VDO_ID_TYPE_PD_PERIPH 2
#define VDO_ID_TYPE_PASS_CBL 3
#define VDO_ID_TYPE_ACTI_CBL 4
#define VDO_ID_TYPE_ALTERNATE 5
/* VDM Discover Mode Caps [From device (UFP_U) to host (DFP_U)] */
#define VDO_DP_UFP_D BIT(0) /* DisplayPort Sink */
#define VDO_DP_DFP_D BIT(1) /* DisplayPort Source */
@@ -376,6 +394,47 @@ enum bm92t_state_type {
DOCK_CONFIG_HANDLED
};
struct __packed pd_object {
unsigned int amp:10;
unsigned int volt:10;
unsigned int info:10;
unsigned int type:2;
};
struct __packed rd_object {
unsigned int max_amp:10;
unsigned int op_amp:10;
unsigned int info:6;
unsigned int usb_comms:1;
unsigned int mismatch:1;
unsigned int obj_no:4;
};
struct __packed vd_object {
unsigned int vid:16;
unsigned int rsvd:10;
unsigned int modal:1;
unsigned int type:3;
unsigned int ufp:1;
unsigned int dfp:1;
unsigned int xid;
unsigned int bcd:16;
unsigned int pid:16;
unsigned int prod_type;
};
struct bm92t_device {
int pdo_no;
unsigned int charging_limit;
bool drd_support;
bool is_nintendo_dock;
struct pd_object pdo;
struct vd_object vdo;
};
struct bm92t_platform_data {
int vconn_en_gpio;
@@ -393,14 +452,6 @@ struct bm92t_platform_data {
unsigned int pd_15v_current_limit;
};
struct bm92t_device {
int pdo_no;
unsigned int charging_limit;
bool drd_support;
bool is_nintendo_dock;
u32 pdo;
};
struct bm92t_info {
struct i2c_client *i2c_client;
struct bm92t_platform_data *pdata;
@@ -411,10 +462,7 @@ struct bm92t_info {
int state;
bool first_init;
struct typec_port *port;
struct typec_partner *partner;
struct usb_pd_identity partner_identity;
struct extcon_dev *edev;
struct delayed_work oneshot_work;
struct delayed_work power_work;
@@ -432,7 +480,9 @@ struct bm92t_info {
struct bm92t_device cable;
struct usb_role_switch *role_sw;
struct fwnode_handle *connector_fwnode;
#ifdef CONFIG_SWITCH
struct switch_dev pd_switch;
#endif
};
static const char * const states[] = {
@@ -464,6 +514,28 @@ static const char * const states[] = {
"DOCK_CONFIG_HANDLED"
};
static const unsigned int bm92t_extcon_cable[] = {
EXTCON_USB_HOST, /* Id */
EXTCON_USB, /* Vbus */
EXTCON_CHG_USB_PD, /* USB-PD */
EXTCON_DISP_DP, /* DisplayPort. Handled by HPD so not used. */
EXTCON_NONE
};
struct bm92t_extcon_cables {
unsigned int cable;
char *name;
};
static const struct bm92t_extcon_cables bm92t_extcon_cable_names[] = {
{ EXTCON_USB_HOST, "USB HOST"},
{ EXTCON_USB, "USB"},
{ EXTCON_CHG_USB_PD, "USB-PD"},
{ EXTCON_DISP_DP, "DisplayPort"},
{ EXTCON_NONE, "None"},
{ -1, "Unknown"}
};
/* charger current input limits */
static const unsigned int current_input_limits[] = {
100, 150, 500, 900, 1200, 1500, 2000, 3000
@@ -522,7 +594,7 @@ static int bm92t_write_reg(struct bm92t_info *info,
xfer_msg[0].buf = buf;
dev_dbg(&info->i2c_client->dev,
"write reg cmd = 0x%02X len = %d\n", buf[0], len);
"write reg cmd = 0x%02X len = %d\n", buf[0], len);
return (i2c_transfer(info->i2c_client->adapter, xfer_msg, 1) != 1);
}
@@ -570,8 +642,8 @@ static int bm92t_send_cmd(struct bm92t_info *info, unsigned short *cmd)
ret = bm92t_write_reg(info, msg, 3);
dev_dbg(&info->i2c_client->dev,
"Sent cmd 0x%02X 0x%02X return value %d\n",
_cmd[0], _cmd[1], ret);
"Sent cmd 0x%02X 0x%02X return value %d\n",
_cmd[0], _cmd[1], ret);
return ret;
}
@@ -645,6 +717,7 @@ static int bm92t_handle_dp_config_and_hpd(struct bm92t_info *info)
unsigned char cfg[6] = {OUTGOING_VDM_REG, 0x04,
VDO_DP_SUPPORT | VDO_DP_U_UFP_D,
0x00, 0x00, 0x00};
union extcon_property_value prop;
/* Set primary pin assignment by lanes supported */
unsigned char pin_cfg = (info->pdata->dp_lanes == 4) ?
@@ -653,7 +726,7 @@ static int bm92t_handle_dp_config_and_hpd(struct bm92t_info *info)
/* Read DisplayPort Capabilities */
err = bm92t_read_reg(info, INCOMING_VDM_REG, msg, sizeof(msg));
dev_info(&info->i2c_client->dev,
"DP Pin assignments: %02X %02X\n", msg[2], msg[3]);
"DP Pin assignments: %02X %02X\n", msg[2], msg[3]);
/* Prepare UFP_U as UFP_D configuration */
for (i = 0; i < 2; i++) {
@@ -702,8 +775,9 @@ static int bm92t_handle_dp_config_and_hpd(struct bm92t_info *info)
/* Configure DP Alt mode and start handling HPD */
bm92t_send_cmd(info, &cmd);
info->cable.charging_limit = valid_lanes > info->pdata->dp_lanes ?
info->pdata->dp_lanes : valid_lanes;
prop.intval = valid_lanes > info->pdata->dp_lanes ?
info->pdata->dp_lanes : valid_lanes;
} else {
dev_warn(&info->i2c_client->dev,
"No compatible DP Pin assignment (%d: %02X %02X %02X)!\n",
@@ -860,20 +934,56 @@ ret:
return err;
}
static void bm92t_set_data_role(struct bm92t_info *info,
enum typec_data_role role, bool attached)
static char *bm92t_extcon_cable_get_name(const unsigned int cable)
{
enum usb_role usb_role = USB_ROLE_NONE;
int i, count;
if (attached) {
if (role == TYPEC_HOST)
usb_role = USB_ROLE_HOST;
else
usb_role = USB_ROLE_DEVICE;
count = ARRAY_SIZE(bm92t_extcon_cable_names);
for (i = 0; i < count; i++) {
if (bm92t_extcon_cable_names[i].cable == cable)
return bm92t_extcon_cable_names[i].name;
}
usb_role_switch_set_role(info->role_sw, usb_role);
typec_set_data_role(info->port, role);
return bm92t_extcon_cable_names[count - 1].name;
}
static void bm92t_usb_role_update(struct bm92t_info *info)
{
enum usb_role role;
/* Consider host mode highest priority */
if (extcon_get_state(info->edev, EXTCON_USB_HOST))
role = USB_ROLE_HOST;
else if (extcon_get_state(info->edev, EXTCON_USB))
role = USB_ROLE_DEVICE;
else
role = USB_ROLE_NONE;
if (usb_role_switch_get_role(info->role_sw) != role)
usb_role_switch_set_role(info->role_sw, role);
}
static void bm92t_extcon_cable_update(struct bm92t_info *info,
const unsigned int cable, bool is_attached)
{
int state = extcon_get_state(info->edev, cable);
if (state != is_attached) {
dev_info(&info->i2c_client->dev, "extcon cable (%02d: %s) %s\n",
cable, bm92t_extcon_cable_get_name(cable),
is_attached ? "attached" : "detached");
extcon_set_state(info->edev, cable, is_attached);
}
switch (cable) {
case EXTCON_USB:
case EXTCON_USB_HOST:
bm92t_usb_role_update(info);
break;
default:
break;
}
}
static inline void bm92t_state_machine(struct bm92t_info *info, int state)
@@ -930,9 +1040,15 @@ static void bm92t_power_work(struct work_struct *work)
bm92t_set_current_limit(info, info->cable.charging_limit * 1000u);
info->pd_charging_enabled = true;
extcon_set_state(info->edev, EXTCON_CHG_USB_PD, true);
#ifdef CONFIG_SWITCH
switch_set_state(&info->pd_switch, 1);
#endif
}
static void bm92t_port_set_init_state(struct work_struct *work)
static void bm92t_extcon_cable_set_init_state(struct work_struct *work)
{
struct bm92t_info *info = container_of(to_delayed_work(work),
struct bm92t_info, oneshot_work);
@@ -953,8 +1069,13 @@ static void bm92t_port_set_init_state(struct work_struct *work)
/* Enable DisplayPort Alerts */
bm92t_set_dp_alerts(info, info->pdata->dp_alerts_enable);
/* Initialize states for extcons */
bm92t_extcon_cable_update(info, EXTCON_DISP_DP, false);
bm92t_extcon_cable_update(info, EXTCON_USB_HOST, false);
bm92t_extcon_cable_update(info, EXTCON_USB, false);
dev_info(&info->i2c_client->dev,
"port is set to init state\n");
"extcon cable is set to init state\n");
msleep(100); /* Wait a bit */
queue_work(info->event_wq, &info->work);
@@ -965,16 +1086,16 @@ static bool bm92t_check_pdo(struct bm92t_info *info)
int i, err, pdos_no;
struct device *dev;
unsigned char pdos[29];
u32 pdo[7];
struct pd_object pdo[7];
unsigned int prev_wattage = 0;
unsigned int amperage, voltage, wattage;
unsigned int amperage, voltage, wattage, type;
dev = &info->i2c_client->dev;
memset(&info->cable, 0, sizeof(struct bm92t_device));
err = bm92t_read_reg(info, READ_PDOS_SRC_REG, pdos, sizeof(pdos));
pdos_no = pdos[0] / sizeof(u32);
pdos_no = pdos[0] / sizeof(struct pd_object);
/* Check if errors or no pdo received */
if (err || !pdos_no)
@@ -984,27 +1105,27 @@ static bool bm92t_check_pdo(struct bm92t_info *info)
memcpy(pdo, pdos + 1, pdos[0]);
for (i = 0; i < pdos_no; ++i) {
dev_info(dev, "PDO %d: %4dmA %5dmV %s\n",
i + 1, pdo_fixed_current(pdo[i]),
pdo_fixed_voltage(pdo[i]),
(pdo[i] & PDO_FIXED_DATA_SWAP) ? "DRD" : "No DRD");
i + 1, pdo[i].amp * 10, pdo[i].volt * 50,
(pdo[i].info & PDO_INFO_DR_DATA) ? "DRD" : "No DRD");
}
if (pdo[0] & PDO_FIXED_DATA_SWAP)
if (pdo[0].info & PDO_INFO_DR_DATA)
info->cable.drd_support = true;
/* Check for dock mode */
if (!info->pdata->dock_power_limit_disable &&
pdos_no == 2 &&
pdo_fixed_voltage(pdo[0]) == DOCK_ID_VOLTAGE_MV &&
pdo_fixed_current(pdo[0]) == DOCK_ID_CURRENT_MA) {
(pdo[0].volt * 50) == DOCK_ID_VOLTAGE_MV &&
(pdo[0].amp * 10) == DOCK_ID_CURRENT_MA) {
/* Only accept 15V, >= 2.6A for dock mode. */
if (pdo_type(pdo[1]) == PDO_TYPE_FIXED &&
pdo_fixed_voltage(pdo[1]) == DOCK_INPUT_VOLTAGE_MV &&
pdo_fixed_current(pdo[1]) >= DOCK_INPUT_CURRENT_LIMIT_MIN_MA &&
pdo_fixed_current(pdo[1]) <= DOCK_INPUT_CURRENT_LIMIT_MAX_MA) {
if (pdo[1].type == PDO_TYPE_FIXED &&
(pdo[1].volt * 50) == DOCK_INPUT_VOLTAGE_MV &&
(pdo[1].amp * 10) >= DOCK_INPUT_CURRENT_LIMIT_MIN_MA &&
(pdo[1].amp * 10) <= DOCK_INPUT_CURRENT_LIMIT_MAX_MA) {
dev_info(dev, "Device in Nintendo mode\n");
info->cable.pdo_no = 2;
info->cable.pdo = pdo[1];
memcpy(&info->cable.pdo, &pdo[1],
sizeof(struct pd_object));
return 1;
}
@@ -1014,8 +1135,9 @@ static bool bm92t_check_pdo(struct bm92t_info *info)
/* Not in dock mode. Check for max possible wattage */
for (i = 0; i < pdos_no; ++i) {
voltage = pdo_fixed_voltage(pdo[i]);
amperage = pdo_fixed_current(pdo[i]);
type = pdo[i].type;
voltage = pdo[i].volt * 50;
amperage = pdo[i].amp * 10;
wattage = voltage * amperage;
/* Only USB-PD defined voltages with max 15V. */
@@ -1030,16 +1152,17 @@ static bool bm92t_check_pdo(struct bm92t_info *info)
}
/* Only accept <= 3A and select max wattage with max voltage. */
if (pdo_type(pdo[i]) == PDO_TYPE_FIXED &&
if (type == PDO_TYPE_FIXED &&
amperage >= PD_INPUT_CURRENT_LIMIT_MIN_MA &&
amperage <= PD_INPUT_CURRENT_LIMIT_MAX_MA) {
if (wattage > prev_wattage ||
(voltage > pdo_fixed_voltage(info->cable.pdo) &&
(voltage > (info->cable.pdo.volt * 50) &&
wattage && wattage == prev_wattage) ||
(!info->cable.pdo_no && !amperage && voltage == 5000)) {
prev_wattage = wattage;
info->cable.pdo_no = i + 1;
info->cable.pdo = pdo[i];
memcpy(&info->cable.pdo, &pdo[i],
sizeof(struct pd_object));
}
}
}
@@ -1054,25 +1177,26 @@ static bool bm92t_check_pdo(struct bm92t_info *info)
static int bm92t_send_rdo(struct bm92t_info *info)
{
u32 rdo;
struct rd_object rdo = { 0 };
unsigned char msg[6] = { SET_RDO_REG, 0x04, 0x00, 0x00, 0x00, 0x00};
unsigned short cmd = SEND_RDO_CMD;
/* Calculate operating current */
bm92t_calculate_current_limit(info, pdo_fixed_voltage(info->cable.pdo),
pdo_fixed_current(info->cable.pdo));
bm92t_calculate_current_limit(info, info->cable.pdo.volt * 50,
info->cable.pdo.amp * 10);
dev_info(&info->i2c_client->dev,
"Requesting %d: min %dmA, max %4dmA, %5dmV\n",
info->cable.pdo_no, info->cable.charging_limit,
pdo_fixed_current(info->cable.pdo),
pdo_fixed_voltage(info->cable.pdo));
"Requesting %d: min %dmA, max %4dmA, %5dmV\n",
info->cable.pdo_no, info->cable.charging_limit,
info->cable.pdo.amp * 10,
info->cable.pdo.volt * 50);
rdo = RDO_FIXED(info->cable.pdo_no, info->cable.charging_limit,
pdo_fixed_current(info->cable.pdo),
RDO_USB_COMM | RDO_NO_SUSPEND);
rdo.usb_comms = 1;
rdo.obj_no = info->cable.pdo_no;
rdo.max_amp = info->cable.pdo.amp;
rdo.op_amp = info->cable.charging_limit / 10;
memcpy(&msg[2], &rdo, sizeof(u32));
memcpy(&msg[2], &rdo, sizeof(struct rd_object));
if (bm92t_write_reg(info, msg, sizeof(msg))) {
dev_err(&info->i2c_client->dev, "Send RDO failure!\n");
@@ -1157,84 +1281,36 @@ static void bm92t_usbhub_dp_sleep(struct bm92t_info *info, bool sleep)
}
static void bm92t_print_dp_dev_info(struct device *dev,
struct usb_pd_identity *id)
struct vd_object *vdo)
{
u32 idh = id->id_header;
dev_info(dev, "Connected PD device:\n");
dev_info(dev, "VID: %04X, PID: %04X\n",
PD_IDH_VID(idh), PD_PRODUCT_PID(id->product));
dev_info(dev, "VID: %04X, PID: %04X\n", vdo->vid, vdo->pid);
switch (PD_IDH_PTYPE(idh)) {
case IDH_PTYPE_UNDEF:
switch (vdo->type) {
case VDO_ID_TYPE_NONE:
dev_info(dev, "Type: Undefined\n");
break;
case IDH_PTYPE_HUB:
case VDO_ID_TYPE_PD_HUB:
dev_info(dev, "Type: PD HUB\n");
break;
case IDH_PTYPE_PERIPH:
case VDO_ID_TYPE_PD_PERIPH:
dev_info(dev, "Type: PD Peripheral\n");
break;
case IDH_PTYPE_PCABLE:
case VDO_ID_TYPE_PASS_CBL:
dev_info(dev, "Type: Passive Cable\n");
break;
case IDH_PTYPE_ACABLE:
case VDO_ID_TYPE_ACTI_CBL:
dev_info(dev, "Type: Active Cable\n");
break;
case IDH_PTYPE_AMA:
case VDO_ID_TYPE_ALTERNATE:
dev_info(dev, "Type: Alternate Mode Adapter\n");
break;
default:
dev_info(dev, "Type: Unknown (%d)\n", PD_IDH_PTYPE(idh));
dev_info(dev, "Type: Unknown (%d)\n", vdo->type);
break;
}
}
static void bm92t_connect(struct bm92t_info *info, unsigned short status1_data)
{
struct typec_partner_desc desc;
if (info->partner)
return;
memset(&info->partner_identity, 0, sizeof(info->partner_identity));
desc.usb_pd = true;
desc.accessory = TYPEC_ACCESSORY_NONE;
desc.identity = &info->partner_identity;
info->partner = typec_register_partner(info->port, &desc);
if (IS_ERR(info->partner)) {
dev_err(&info->i2c_client->dev, "failed to register partner\n");
info->partner = NULL;
return;
}
typec_set_pwr_opmode(info->port, TYPEC_PWR_MODE_PD);
typec_set_pwr_role(info->port, bm92t_is_dfp(status1_data) ?
TYPEC_SOURCE : TYPEC_SINK);
typec_set_vconn_role(info->port, TYPEC_SOURCE);
bm92t_set_data_role(info, bm92t_is_dfp(status1_data) ?
TYPEC_HOST : TYPEC_DEVICE, true);
}
static void bm92t_disconnect(struct bm92t_info *info, unsigned short status1_data)
{
if (info->partner) {
typec_unregister_partner(info->partner);
info->partner = NULL;
}
typec_set_pwr_opmode(info->port, TYPEC_PWR_MODE_USB);
typec_set_pwr_role(info->port, TYPEC_SINK);
typec_set_vconn_role(info->port, TYPEC_SINK);
bm92t_set_data_role(info, TYPEC_DEVICE, false);
if (info->connector_fwnode)
drm_connector_oob_hotplug_event(info->connector_fwnode,
connector_status_disconnected);
}
static void bm92t_event_handler(struct work_struct *work)
{
static bool sys_reset;
@@ -1242,7 +1318,8 @@ static void bm92t_event_handler(struct work_struct *work)
int i, err;
struct bm92t_info *info;
struct device *dev;
u32 curr_pdo, curr_rdo;
struct pd_object curr_pdo;
struct rd_object curr_rdo;
unsigned short cmd;
unsigned short alert_data;
unsigned short status1_data;
@@ -1297,7 +1374,9 @@ static void bm92t_event_handler(struct work_struct *work)
if (err) {
dev_err(dev, "Internal error occurred. Ecode = %d\n", err);
bm92t_state_machine(info, INIT_STATE);
bm92t_disconnect(info, status1_data);
bm92t_extcon_cable_update(info, EXTCON_DISP_DP, false);
bm92t_extcon_cable_update(info, EXTCON_USB_HOST, false);
bm92t_extcon_cable_update(info, EXTCON_USB, false);
bm92t_set_vbus_enable(info, false);
if (bm92t_is_plugged(status1_data) ||
alert_data & ALERT_SNK_FAULT ||
@@ -1341,11 +1420,18 @@ src_fault:
if (info->pd_charging_enabled) {
bm92t_set_current_limit(info, 0);
info->pd_charging_enabled = false;
bm92t_extcon_cable_update(info,
EXTCON_CHG_USB_PD, false);
#ifdef CONFIG_SWITCH
switch_set_state(&info->pd_switch, 0);
#endif
}
/* Reset USB modes and state */
retries_usbhub = 10;
bm92t_disconnect(info, status1_data);
bm92t_extcon_cable_update(info, EXTCON_DISP_DP, false);
bm92t_extcon_cable_update(info, EXTCON_USB_HOST, false);
bm92t_extcon_cable_update(info, EXTCON_USB, false);
bm92t_state_machine(info, INIT_STATE);
goto ret;
} else if (status1_data & STATUS1_SRC_MODE && /* OTG plug event */
@@ -1353,7 +1439,9 @@ src_fault:
/* Enable VBUS for sourcing power to OTG device */
bm92t_set_vbus_enable(info, true);
bm92t_connect(info, status1_data);
/* Set USB to host mode */
bm92t_extcon_cable_update(info, EXTCON_USB, false);
bm92t_extcon_cable_update(info, EXTCON_USB_HOST, true);
goto ret;
} else if (alert_data & ALERT_CONTRACT) {
/* When there's a plug-in wake-up, check if a new
@@ -1408,14 +1496,13 @@ handle_new_state:
goto init_contract_out;
}
/* Register partner if not already */
bm92t_connect(info, status1_data);
/* Negotiate new power profile */
if (!bm92t_check_pdo(info)) {
dev_info(dev, "Power Negotiation not supported\n");
bm92t_state_machine(info, INIT_STATE);
msleep(550); /* WAR: charger good power test */
bm92t_extcon_cable_update(info,
EXTCON_USB, true);
goto init_contract_out;
}
@@ -1431,7 +1518,7 @@ init_contract_out:
/* Check if forced workqueue and unplugged */
if (!alert_data && !bm92t_is_plugged(status1_data))
bm92t_disconnect(info, status1_data);
bm92t_extcon_cable_update(info, EXTCON_USB, false);
break;
case NEW_PDO:
@@ -1442,20 +1529,19 @@ init_contract_out:
/* Check PDO/RDO */
err = bm92t_read_reg(info, CURRENT_PDO_REG,
pdo, sizeof(pdo));
memcpy(&curr_pdo, &pdo[1], sizeof(u32));
memcpy(&curr_pdo, &pdo[1], sizeof(struct pd_object));
err = bm92t_read_reg(info, CURRENT_RDO_REG,
rdo, sizeof(rdo));
memcpy(&curr_rdo, &rdo[1], sizeof(u32));
memcpy(&curr_rdo, &rdo[1], sizeof(struct rd_object));
dev_info(dev, "New PD Contract:\n");
dev_info(dev, "PDO: %d: %dmA, %dmV\n",
info->cable.pdo_no, pdo_fixed_current(curr_pdo),
pdo_fixed_voltage(curr_pdo));
info->cable.pdo_no, curr_pdo.amp * 10,
curr_pdo.volt * 50);
dev_info(dev, "RDO: op %dmA, %dmA max\n",
rdo_op_current(curr_rdo),
rdo_max_current(curr_rdo));
curr_rdo.op_amp * 10, curr_rdo.max_amp * 10);
if (curr_rdo & RDO_CAP_MISMATCH)
if (curr_rdo.mismatch)
dev_err(dev, "PD mismatch!\n");
cmd = PS_RDY_CMD;
@@ -1466,6 +1552,7 @@ init_contract_out:
case PS_RDY_SENT:
if (bm92t_is_success(alert_data)) {
bm92t_extcon_cable_update(info, EXTCON_USB_HOST, true);
schedule_delayed_work(&info->power_work,
msecs_to_jiffies(2000));
@@ -1493,15 +1580,15 @@ init_contract_out:
bm92t_is_plugged(status1_data) &&
bm92t_is_lastcmd_ok(info, "DR_SWAP_CMD", status1_data) &&
bm92t_is_dfp(status1_data)) {
bm92t_set_data_role(info, TYPEC_HOST, true);
bm92t_send_vdm(info, vdm_discover_id_msg,
sizeof(vdm_discover_id_msg));
bm92t_state_machine(info, VDM_DISC_ID_SENT);
} else if (bm92t_is_plugged(status1_data) &&
bm92t_lastcmd_status(status1_data) ==
LASTCMD_REJECTED) {
LASTCMD_REJECTED) {
/* UFP rejected data role swap. */
bm92t_set_data_role(info, TYPEC_DEVICE, true);
bm92t_extcon_cable_update(info, EXTCON_USB_HOST, false);
bm92t_extcon_cable_update(info, EXTCON_USB, true);
bm92t_state_machine(info, INIT_STATE);
}
break;
@@ -1531,31 +1618,15 @@ init_contract_out:
err = bm92t_read_reg(info, INCOMING_VDM_REG,
vdm, sizeof(vdm));
/* VDM layout: [0]=size, [1]=cmd/ack, [2]=structured,
* [3-4]=SVID, [5..]=VDO objects
*/
info->partner_identity.id_header =
vdm[5] | (vdm[6] << 8) |
(vdm[7] << 16) | (vdm[8] << 24);
info->partner_identity.cert_stat =
vdm[9] | (vdm[10] << 8) |
(vdm[11] << 16) | (vdm[12] << 24);
info->partner_identity.product =
vdm[13] | (vdm[14] << 8) |
(vdm[15] << 16) | (vdm[16] << 24);
memcpy(&info->cable.vdo, &vdm[5],
sizeof(struct vd_object));
bm92t_print_dp_dev_info(dev, &info->partner_identity);
if (info->partner)
typec_partner_set_identity(info->partner);
bm92t_print_dp_dev_info(dev, &info->cable.vdo);
/* Check if Nintendo dock. */
if (!(PD_IDH_PTYPE(info->partner_identity.id_header)
== IDH_PTYPE_AMA &&
PD_IDH_VID(info->partner_identity.id_header)
== VID_NINTENDO &&
PD_PRODUCT_PID(info->partner_identity.product)
== PID_NIN_DOCK)) {
if (!(info->cable.vdo.type == VDO_ID_TYPE_ALTERNATE &&
info->cable.vdo.vid == VID_NINTENDO &&
info->cable.vdo.pid == PID_NIN_DOCK)) {
dev_info(dev, "VID/PID not Nintendo Dock\n");
if (info->pdata->dp_disable) {
@@ -1709,12 +1780,8 @@ init_contract_out:
} else
bm92t_state_machine(info,
DOCK_CONFIG_HANDLED);
/* Report DP connection to DRM */
if (info->connector_fwnode)
drm_connector_oob_hotplug_event(
info->connector_fwnode,
connector_status_connected);
bm92t_extcon_cable_update(info,
EXTCON_DISP_DP, true);
}
}
break;
@@ -1739,9 +1806,11 @@ init_contract_out:
vdm[7] == (VDM_NCMD_DEVICE_STATE + 1)) {
/* Check if USB HUB is supported */
if (vdm[11] & 0x02) {
bm92t_set_data_role(info, TYPEC_DEVICE, false);
bm92t_extcon_cable_update(info,
EXTCON_USB_HOST, false);
msleep(500);
bm92t_set_data_role(info, TYPEC_DEVICE, true);
bm92t_extcon_cable_update(info,
EXTCON_USB, true);
dev_warn(dev, "Dock has old FW!\n");
}
dev_info(dev, "Dock state: %02X %02X %02X %02X\n",
@@ -1875,21 +1944,6 @@ static void bm92t_remove(struct i2c_client *client)
{
struct bm92t_info *info = i2c_get_clientdata(client);
if (client->irq > 0)
free_irq(client->irq, info);
cancel_delayed_work_sync(&info->oneshot_work);
cancel_delayed_work_sync(&info->power_work);
cancel_work_sync(&info->work);
destroy_workqueue(info->event_wq);
if (info->partner)
typec_unregister_partner(info->partner);
typec_unregister_port(info->port);
usb_role_switch_put(info->role_sw);
fwnode_handle_put(info->connector_fwnode);
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(info->debugfs_root);
#endif
@@ -2293,57 +2347,12 @@ static struct bm92t_platform_data *bm92t_parse_dt(struct device *dev)
}
#endif
static int bm92t_try_role(struct typec_port *port, int role)
{
return -EOPNOTSUPP;
}
static int bm92t_dr_set(struct typec_port *port, enum typec_data_role role)
{
struct bm92t_info *info = typec_get_drvdata(port);
unsigned short cmd = DR_SWAP_CMD;
return bm92t_send_cmd(info, &cmd);
}
static int bm92t_pr_set(struct typec_port *port, enum typec_role role)
{
struct bm92t_info *info = typec_get_drvdata(port);
unsigned short cmd = PR_SWAP_CMD;
return bm92t_send_cmd(info, &cmd);
}
static int bm92t_port_type_set(struct typec_port *port,
enum typec_port_type type)
{
struct bm92t_info *info = typec_get_drvdata(port);
switch (type) {
case TYPEC_PORT_SNK:
return bm92t_set_source_mode(info, SPDSRC12_OFF);
case TYPEC_PORT_SRC:
case TYPEC_PORT_DRP:
return bm92t_set_source_mode(info, SPDSRC12_ON);
default:
return -EINVAL;
}
}
static const struct typec_operations bm92t_ops = {
.try_role = bm92t_try_role,
.dr_set = bm92t_dr_set,
.pr_set = bm92t_pr_set,
.port_type_set = bm92t_port_type_set,
};
static int bm92t_probe(struct i2c_client *client)
{
struct bm92t_info *info;
struct fwnode_handle *connector;
struct fwnode_handle *ep, *remote;
struct regulator *batt_chg_reg;
struct regulator *vbus_reg, *vbus_src_reg;
struct typec_capability typec_cap = { };
int err;
unsigned short reg_value;
@@ -2409,25 +2418,25 @@ static int bm92t_probe(struct i2c_client *client)
gpio_direction_output(info->pdata->vconn_en_gpio, 0);
}
/* Get connector fwnode (st style) */
connector = device_get_named_child_node(&client->dev, "connector");
if (!connector) {
dev_err(&client->dev, "Connector node not found\n");
ep = fwnode_graph_get_endpoint_by_id(
dev_fwnode(&client->dev), 0, 0, 0);
if (!ep) {
dev_err(&client->dev, "Endpoint not found\n");
return -ENODEV;
}
fw_devlink_purge_absent_suppliers(connector);
info->role_sw = fwnode_usb_role_switch_get(connector);
if (IS_ERR(info->role_sw)) {
err = PTR_ERR(info->role_sw);
dev_err(&client->dev, "Failed to get usb role switch: %d\n", err);
goto err_put_connector;
remote = fwnode_graph_get_remote_endpoint(ep);
if (!remote) {
dev_err(&client->dev, "Remote not found\n");
return -ENODEV;
}
/* Optional DP connector fwnode for HPD reporting */
info->connector_fwnode = fwnode_find_reference(connector, "displayport", 0);
if (IS_ERR(info->connector_fwnode))
info->connector_fwnode = NULL;
info->role_sw = fwnode_usb_role_switch_get(remote);
if (IS_ERR(info->role_sw)) {
err = PTR_ERR(info->role_sw);
dev_err(&client->dev, "Failed to retrieve fwnode: %d\n", err);
return err;
}
i2c_set_clientdata(client, info);
@@ -2440,29 +2449,25 @@ static int bm92t_probe(struct i2c_client *client)
info->state = INIT_STATE;
info->first_init = true;
/* Register typec port */
typec_cap.revision = USB_TYPEC_REV_1_2;
typec_cap.pd_revision = 0x200;
typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
typec_cap.type = TYPEC_PORT_DRP;
typec_cap.data = TYPEC_PORT_DRD;
typec_cap.driver_data = info;
typec_cap.ops = &bm92t_ops;
typec_cap.fwnode = connector;
/* Allocate extcon */
info->edev = devm_extcon_dev_allocate(&client->dev, bm92t_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&client->dev, "kzalloc error\n");
return -ENOMEM;
}
info->port = typec_register_port(&client->dev, &typec_cap);
if (IS_ERR(info->port)) {
err = PTR_ERR(info->port);
dev_err(&client->dev, "failed to register typec port: %d\n", err);
goto err_put_role;
/* Register extcon */
err = devm_extcon_dev_register(&client->dev, info->edev);
if (err < 0) {
dev_err(&client->dev, "Cannot register extcon device\n");
return err;
}
/* Create workqueue */
info->event_wq = create_singlethread_workqueue("bm92t-event-queue");
if (!info->event_wq) {
dev_err(&client->dev, "Cannot create work queue\n");
err = -ENOMEM;
goto err_unreg_port;
return -ENOMEM;
}
err = bm92t_read_reg(info, FW_TYPE_REG,
@@ -2478,8 +2483,7 @@ static int bm92t_probe(struct i2c_client *client)
if (info->fw_revision <= 0x644) {
dev_err(&client->dev, "fw revision not supported\n");
err = -EINVAL;
goto err_destroy_wq;
return -EINVAL;
}
/* Disable Source mode at boot */
@@ -2487,29 +2491,27 @@ static int bm92t_probe(struct i2c_client *client)
INIT_WORK(&info->work, bm92t_event_handler);
INIT_DELAYED_WORK(&info->oneshot_work,
bm92t_port_set_init_state);
bm92t_extcon_cable_set_init_state);
INIT_DELAYED_WORK(&info->power_work, bm92t_power_work);
if (client->irq > 0) {
/*
* Level-triggered (active-low) IRQ wired to BM92T36 ALERT.
* Use a threaded handler with IRQF_ONESHOT so the line is
* masked while bm92t_interrupt_handler runs i2c reads, then
* re-enabled after the work clears the controller's status.
* Without IRQF_ONESHOT this would re-enter immediately on
* the still-asserted line and "irq nobody cared" disable it.
*/
if (request_threaded_irq(client->irq, NULL,
bm92t_interrupt_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT, "bm92t",
if (request_irq(client->irq, bm92t_interrupt_handler,
IRQF_TRIGGER_LOW, "bm92t",
info)) {
dev_err(&client->dev, "Request irq failed\n");
err = -EBUSY;
goto err_destroy_wq;
destroy_workqueue(info->event_wq);
return -EBUSY;
}
}
#ifdef CONFIG_SWITCH
info->pd_switch.name = pd_switch_name;
err = switch_dev_register(&info->pd_switch);
if (err)
dev_err(&client->dev, "failed to register pd switch, err=%d\n", err);
#endif
#ifdef CONFIG_DEBUG_FS
bm92t_debug_init(info);
#endif
@@ -2526,18 +2528,7 @@ static int bm92t_probe(struct i2c_client *client)
dev_info(&client->dev, "init done\n");
fwnode_handle_put(connector);
return 0;
err_destroy_wq:
destroy_workqueue(info->event_wq);
err_unreg_port:
typec_unregister_port(info->port);
err_put_role:
usb_role_switch_put(info->role_sw);
err_put_connector:
fwnode_handle_put(connector);
return err;
}
#ifdef CONFIG_PM
@@ -2653,5 +2644,5 @@ static void __exit bm92t_exit(void)
module_exit(bm92t_exit);
MODULE_AUTHOR("CTCaer <ctcaer@gmail.com>");
MODULE_DESCRIPTION("Rohm BM92TXX USB Type-C and PD controller driver");
MODULE_DESCRIPTION("BM92TXX USB PD/DP management IC driver");
MODULE_LICENSE("GPL");