commit
ae27120c31
@ -0,0 +1,588 @@ |
||||
/* |
||||
* Google Spring board device tree source |
||||
* |
||||
* Copyright (c) 2013 Google, Inc |
||||
* Copyright (c) 2014 SUSE LINUX Products GmbH |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0 |
||||
*/ |
||||
|
||||
/dts-v1/; |
||||
#include <dt-bindings/gpio/gpio.h> |
||||
#include <dt-bindings/interrupt-controller/irq.h> |
||||
#include <dt-bindings/input/input.h> |
||||
#include "exynos5250.dtsi" |
||||
|
||||
/ { |
||||
model = "Google Spring"; |
||||
compatible = "google,spring", "samsung,exynos5250", "samsung,exynos5"; |
||||
|
||||
aliases { |
||||
i2c0 = "/i2c@12C60000"; |
||||
i2c1 = "/i2c@12C70000"; |
||||
i2c2 = "/i2c@12C80000"; |
||||
i2c3 = "/i2c@12C90000"; |
||||
i2c4 = "/i2c@12CA0000"; |
||||
i2c5 = "/i2c@12CB0000"; |
||||
i2c6 = "/i2c@12CC0000"; |
||||
i2c7 = "/i2c@12CD0000"; |
||||
i2c104 = &cros_ec_ldo_tunnel; |
||||
spi0 = "/spi@12d20000"; |
||||
spi1 = "/spi@12d30000"; |
||||
spi2 = "/spi@12d40000"; |
||||
spi3 = "/spi@131a0000"; |
||||
spi4 = "/spi@131b0000"; |
||||
mmc0 = "/mmc@12000000"; |
||||
serial0 = "/serial@12C30000"; |
||||
console = "/serial@12C30000"; |
||||
i2s = "/sound@3830000"; |
||||
}; |
||||
|
||||
memory { |
||||
reg = <0x40000000 0x80000000>; |
||||
}; |
||||
|
||||
flash@0 { |
||||
spl { /* spl size override */ |
||||
size = <0x8000>; |
||||
}; |
||||
}; |
||||
|
||||
chosen { |
||||
bootargs = "console=tty1"; |
||||
stdout-path = "serial3:115200n8"; |
||||
}; |
||||
|
||||
board-rev { |
||||
compatible = "google,board-revision"; |
||||
google,board-rev-gpios = <&gpy4 0 0>, <&gpy4 1 0>, |
||||
<&gpy4 2 0>; |
||||
}; |
||||
|
||||
mmc@12200000 { |
||||
samsung,bus-width = <8>; |
||||
samsung,timing = <1 3 3>; |
||||
samsung,removable = <0>; |
||||
}; |
||||
|
||||
mmc@12210000 { |
||||
status = "disabled"; |
||||
}; |
||||
|
||||
mmc@12220000 { |
||||
/* MMC2 pins are used as GPIO for eDP bridge */ |
||||
status = "disabled"; |
||||
}; |
||||
|
||||
mmc@12230000 { |
||||
status = "disabled"; |
||||
}; |
||||
|
||||
ehci@12110000 { |
||||
samsung,vbus-gpio = <&gpx1 1 GPIO_ACTIVE_HIGH>; |
||||
status = "okay"; |
||||
}; |
||||
|
||||
xhci@12000000 { |
||||
samsung,vbus-gpio = <&gpx2 7 GPIO_ACTIVE_HIGH>; |
||||
}; |
||||
|
||||
spi@12d30000 { |
||||
spi-max-frequency = <50000000>; |
||||
firmware_storage_spi: flash@0 { |
||||
compatible = "spi-flash"; |
||||
reg = <0>; |
||||
}; |
||||
}; |
||||
|
||||
tmu@10060000 { |
||||
samsung,min-temp = <25>; |
||||
samsung,max-temp = <125>; |
||||
samsung,start-warning = <95>; |
||||
samsung,start-tripping = <105>; |
||||
samsung,hw-tripping = <110>; |
||||
samsung,efuse-min-value = <40>; |
||||
samsung,efuse-value = <55>; |
||||
samsung,efuse-max-value = <100>; |
||||
samsung,slope = <274761730>; |
||||
samsung,dc-value = <25>; |
||||
}; |
||||
|
||||
fimd@14400000 { |
||||
samsung,vl-freq = <60>; |
||||
samsung,vl-col = <1366>; |
||||
samsung,vl-row = <768>; |
||||
samsung,vl-width = <1366>; |
||||
samsung,vl-height = <768>; |
||||
|
||||
samsung,vl-clkp; |
||||
samsung,vl-dp; |
||||
samsung,vl-hsp; |
||||
samsung,vl-vsp; |
||||
|
||||
samsung,vl-bpix = <4>; |
||||
|
||||
samsung,vl-hspw = <32>; |
||||
samsung,vl-hbpd = <80>; |
||||
samsung,vl-hfpd = <48>; |
||||
samsung,vl-vspw = <5>; |
||||
samsung,vl-vbpd = <14>; |
||||
samsung,vl-vfpd = <3>; |
||||
samsung,vl-cmd-allow-len = <0xf>; |
||||
|
||||
samsung,winid = <0>; |
||||
samsung,interface-mode = <1>; |
||||
samsung,dp-enabled = <1>; |
||||
samsung,dual-lcd-enabled = <0>; |
||||
}; |
||||
|
||||
dp@145b0000 { |
||||
samsung,lt-status = <0>; |
||||
|
||||
samsung,master-mode = <0>; |
||||
samsung,bist-mode = <0>; |
||||
samsung,bist-pattern = <0>; |
||||
samsung,h-sync-polarity = <0>; |
||||
samsung,v-sync-polarity = <0>; |
||||
samsung,interlaced = <0>; |
||||
samsung,color-space = <0>; |
||||
samsung,dynamic-range = <0>; |
||||
samsung,ycbcr-coeff = <0>; |
||||
samsung,color-depth = <1>; |
||||
}; |
||||
}; |
||||
|
||||
&i2c_0 { |
||||
status = "okay"; |
||||
samsung,i2c-sda-delay = <100>; |
||||
samsung,i2c-max-bus-freq = <378000>; |
||||
|
||||
s5m8767-pmic@66 { |
||||
compatible = "samsung,s5m8767-pmic"; |
||||
reg = <0x66>; |
||||
interrupt-parent = <&gpx3>; |
||||
wakeup-source; |
||||
|
||||
s5m8767,pmic-buck-dvs-gpios = <&gpd1 0 GPIO_ACTIVE_LOW>, /* DVS1 */ |
||||
<&gpd1 1 GPIO_ACTIVE_LOW>, /* DVS2 */ |
||||
<&gpd1 2 GPIO_ACTIVE_LOW>; /* DVS3 */ |
||||
|
||||
s5m8767,pmic-buck-ds-gpios = <&gpx2 3 GPIO_ACTIVE_LOW>, /* SET1 */ |
||||
<&gpx2 4 GPIO_ACTIVE_LOW>, /* SET2 */ |
||||
<&gpx2 5 GPIO_ACTIVE_LOW>; /* SET3 */ |
||||
|
||||
/* |
||||
* The following arrays of DVS voltages are not used, since we are |
||||
* not using GPIOs to control PMIC bucks, but they must be defined |
||||
* to please the driver. |
||||
*/ |
||||
s5m8767,pmic-buck2-dvs-voltage = <1350000>, <1300000>, |
||||
<1250000>, <1200000>, |
||||
<1150000>, <1100000>, |
||||
<1000000>, <950000>; |
||||
|
||||
s5m8767,pmic-buck3-dvs-voltage = <1100000>, <1100000>, |
||||
<1100000>, <1100000>, |
||||
<1000000>, <1000000>, |
||||
<1000000>, <1000000>; |
||||
|
||||
s5m8767,pmic-buck4-dvs-voltage = <1200000>, <1200000>, |
||||
<1200000>, <1200000>, |
||||
<1200000>, <1200000>, |
||||
<1200000>, <1200000>; |
||||
|
||||
clocks { |
||||
compatible = "samsung,s5m8767-clk"; |
||||
#clock-cells = <1>; |
||||
clock-output-names = "en32khz_ap", |
||||
"en32khz_cp", |
||||
"en32khz_bt"; |
||||
}; |
||||
|
||||
regulators { |
||||
ldo4_reg: LDO4 { |
||||
regulator-name = "P1.0V_LDO_OUT4"; |
||||
regulator-min-microvolt = <1000000>; |
||||
regulator-max-microvolt = <1000000>; |
||||
regulator-always-on; |
||||
op_mode = <0>; |
||||
}; |
||||
|
||||
ldo5_reg: LDO5 { |
||||
regulator-name = "P1.8V_LDO_OUT5"; |
||||
regulator-min-microvolt = <1800000>; |
||||
regulator-max-microvolt = <1800000>; |
||||
regulator-always-on; |
||||
op_mode = <0>; |
||||
}; |
||||
|
||||
ldo6_reg: LDO6 { |
||||
regulator-name = "vdd_mydp"; |
||||
regulator-min-microvolt = <1200000>; |
||||
regulator-max-microvolt = <1200000>; |
||||
regulator-always-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
ldo7_reg: LDO7 { |
||||
regulator-name = "P1.1V_LDO_OUT7"; |
||||
regulator-min-microvolt = <1100000>; |
||||
regulator-max-microvolt = <1100000>; |
||||
regulator-always-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
ldo8_reg: LDO8 { |
||||
regulator-name = "P1.0V_LDO_OUT8"; |
||||
regulator-min-microvolt = <1000000>; |
||||
regulator-max-microvolt = <1000000>; |
||||
regulator-always-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
ldo10_reg: LDO10 { |
||||
regulator-name = "P1.8V_LDO_OUT10"; |
||||
regulator-min-microvolt = <1800000>; |
||||
regulator-max-microvolt = <1800000>; |
||||
regulator-always-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
ldo11_reg: LDO11 { |
||||
regulator-name = "P1.8V_LDO_OUT11"; |
||||
regulator-min-microvolt = <1800000>; |
||||
regulator-max-microvolt = <1800000>; |
||||
regulator-always-on; |
||||
op_mode = <0>; |
||||
}; |
||||
|
||||
ldo12_reg: LDO12 { |
||||
regulator-name = "P3.0V_LDO_OUT12"; |
||||
regulator-min-microvolt = <3000000>; |
||||
regulator-max-microvolt = <3000000>; |
||||
regulator-always-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
ldo13_reg: LDO13 { |
||||
regulator-name = "P1.8V_LDO_OUT13"; |
||||
regulator-min-microvolt = <1800000>; |
||||
regulator-max-microvolt = <1800000>; |
||||
regulator-always-on; |
||||
op_mode = <0>; |
||||
}; |
||||
|
||||
ldo14_reg: LDO14 { |
||||
regulator-name = "P1.8V_LDO_OUT14"; |
||||
regulator-min-microvolt = <1800000>; |
||||
regulator-max-microvolt = <1800000>; |
||||
regulator-always-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
ldo15_reg: LDO15 { |
||||
regulator-name = "P1.0V_LDO_OUT15"; |
||||
regulator-min-microvolt = <1000000>; |
||||
regulator-max-microvolt = <1000000>; |
||||
regulator-always-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
ldo16_reg: LDO16 { |
||||
regulator-name = "P1.8V_LDO_OUT16"; |
||||
regulator-min-microvolt = <1800000>; |
||||
regulator-max-microvolt = <1800000>; |
||||
regulator-always-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
ldo17_reg: LDO17 { |
||||
regulator-name = "P1.2V_LDO_OUT17"; |
||||
regulator-min-microvolt = <1200000>; |
||||
regulator-max-microvolt = <1200000>; |
||||
regulator-always-on; |
||||
op_mode = <0>; |
||||
}; |
||||
|
||||
ldo25_reg: LDO25 { |
||||
regulator-name = "vdd_bridge"; |
||||
regulator-min-microvolt = <1200000>; |
||||
regulator-max-microvolt = <1200000>; |
||||
regulator-always-on; |
||||
op_mode = <1>; |
||||
}; |
||||
|
||||
buck1_reg: BUCK1 { |
||||
regulator-name = "vdd_mif"; |
||||
regulator-min-microvolt = <950000>; |
||||
regulator-max-microvolt = <1300000>; |
||||
regulator-always-on; |
||||
regulator-boot-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
buck2_reg: BUCK2 { |
||||
regulator-name = "vdd_arm"; |
||||
regulator-min-microvolt = <850000>; |
||||
regulator-max-microvolt = <1350000>; |
||||
regulator-always-on; |
||||
regulator-boot-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
buck3_reg: BUCK3 { |
||||
regulator-name = "vdd_int"; |
||||
regulator-min-microvolt = <900000>; |
||||
regulator-max-microvolt = <1200000>; |
||||
regulator-always-on; |
||||
regulator-boot-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
buck4_reg: BUCK4 { |
||||
regulator-name = "vdd_g3d"; |
||||
regulator-min-microvolt = <850000>; |
||||
regulator-max-microvolt = <1300000>; |
||||
regulator-boot-on; |
||||
op_mode = <3>; |
||||
}; |
||||
|
||||
buck5_reg: BUCK5 { |
||||
regulator-name = "P1.8V_BUCK_OUT5"; |
||||
regulator-min-microvolt = <1800000>; |
||||
regulator-max-microvolt = <1800000>; |
||||
regulator-always-on; |
||||
regulator-boot-on; |
||||
op_mode = <1>; |
||||
}; |
||||
|
||||
buck6_reg: BUCK6 { |
||||
regulator-name = "P1.2V_BUCK_OUT6"; |
||||
regulator-min-microvolt = <2050000>; |
||||
regulator-max-microvolt = <2050000>; |
||||
regulator-always-on; |
||||
regulator-boot-on; |
||||
op_mode = <0>; |
||||
}; |
||||
|
||||
buck9_reg: BUCK9 { |
||||
regulator-name = "vdd_ummc"; |
||||
regulator-min-microvolt = <950000>; |
||||
regulator-max-microvolt = <3000000>; |
||||
regulator-always-on; |
||||
regulator-boot-on; |
||||
op_mode = <3>; |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
&i2c_1 { |
||||
status = "okay"; |
||||
samsung,i2c-sda-delay = <100>; |
||||
samsung,i2c-max-bus-freq = <378000>; |
||||
}; |
||||
|
||||
&i2c_2 { |
||||
status = "okay"; |
||||
samsung,i2c-sda-delay = <100>; |
||||
samsung,i2c-max-bus-freq = <66000>; |
||||
}; |
||||
|
||||
&i2c_3 { |
||||
status = "okay"; |
||||
samsung,i2c-sda-delay = <100>; |
||||
samsung,i2c-max-bus-freq = <66000>; |
||||
}; |
||||
|
||||
&i2c_4 { |
||||
status = "okay"; |
||||
samsung,i2c-sda-delay = <100>; |
||||
samsung,i2c-max-bus-freq = <66000>; |
||||
clock-frequency = <66000>; |
||||
|
||||
cros_ec: embedded-controller { |
||||
compatible = "google,cros-ec-i2c"; |
||||
reg = <0x1e>; |
||||
interrupts = <6 IRQ_TYPE_NONE>; |
||||
interrupt-parent = <&gpx1>; |
||||
wakeup-source; |
||||
u-boot,i2c-offset-len = <0>; |
||||
ec-interrupt = <&gpx1 6 GPIO_ACTIVE_LOW>; |
||||
cros_ec_ldo_tunnel: cros-ec-ldo-tunnel { |
||||
compatible = "google,cros-ec-ldo-tunnel"; |
||||
#address-cells = <1>; |
||||
#size-cells = <0>; |
||||
power-regulator { |
||||
compatible = "ti,tps65090"; |
||||
reg = <0x48>; |
||||
|
||||
regulators { |
||||
dcdc1 { |
||||
ti,enable-ext-control; |
||||
}; |
||||
dcdc2 { |
||||
ti,enable-ext-control; |
||||
}; |
||||
dcdc3 { |
||||
ti,enable-ext-control; |
||||
}; |
||||
fet1: fet1 { |
||||
regulator-name = "vcd_led"; |
||||
ti,overcurrent-wait = <3>; |
||||
}; |
||||
tps65090_fet2: fet2 { |
||||
regulator-name = "video_mid"; |
||||
regulator-always-on; |
||||
ti,overcurrent-wait = <3>; |
||||
}; |
||||
fet3 { |
||||
regulator-name = "wwan_r"; |
||||
regulator-always-on; |
||||
ti,overcurrent-wait = <3>; |
||||
}; |
||||
fet4 { |
||||
regulator-name = "sdcard"; |
||||
ti,overcurrent-wait = <3>; |
||||
}; |
||||
fet5 { |
||||
regulator-name = "camout"; |
||||
regulator-always-on; |
||||
ti,overcurrent-wait = <3>; |
||||
}; |
||||
fet6: fet6 { |
||||
regulator-name = "lcd_vdd"; |
||||
ti,overcurrent-wait = <3>; |
||||
}; |
||||
tps65090_fet7: fet7 { |
||||
regulator-name = "video_mid_1a"; |
||||
regulator-always-on; |
||||
ti,overcurrent-wait = <3>; |
||||
}; |
||||
ldo1 { |
||||
}; |
||||
ldo2 { |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
&i2c_5 { |
||||
status = "okay"; |
||||
samsung,i2c-sda-delay = <100>; |
||||
samsung,i2c-max-bus-freq = <66000>; |
||||
}; |
||||
|
||||
&i2c_7 { |
||||
status = "okay"; |
||||
samsung,i2c-sda-delay = <100>; |
||||
samsung,i2c-max-bus-freq = <66000>; |
||||
|
||||
ps8622-bridge@8 { |
||||
compatible = "parade,ps8622"; |
||||
reg = <0x8>; |
||||
sleep-gpios = <&gpc3 6 GPIO_ACTIVE_LOW>; |
||||
reset-gpios = <&gpc3 1 GPIO_ACTIVE_LOW>; |
||||
hotplug-gpios = <&gpc3 0 GPIO_ACTIVE_HIGH>; |
||||
power-supply = <&ldo6_reg>; |
||||
parade,regs = /bits/ 8 < |
||||
0x02 0xa1 0x01 /* HPD low */ |
||||
/* |
||||
* SW setting: [1:0] SW output 1.2V voltage is |
||||
* lower to 96% |
||||
*/ |
||||
0x04 0x14 0x01 |
||||
/* RCO SS setting: [5:4] = b01 0.5%, b10 1%, b11 1.5% */ |
||||
0x04 0xe3 0x20 |
||||
0x04 0xe2 0x80 /* [7] RCO SS enable */ |
||||
/* |
||||
* RPHY Setting: [3:2] CDR tune wait cycle before |
||||
* measure for fine tune b00: 1us, |
||||
* 01: 0.5us, 10:2us, 11:4us |
||||
*/ |
||||
0x04 0x8a 0x0c |
||||
0x04 0x89 0x08 /* [3] RFD always on */ |
||||
/* |
||||
* CTN lock in/out: 20000ppm/80000ppm. Lock out 2 times |
||||
*/ |
||||
0x04 0x71 0x2d |
||||
/* 2.7G CDR settings */ |
||||
0x04 0x7d 0x07 /* NOF=40LSB for HBR CDR setting */ |
||||
0x04 0x7b 0x00 /* [1:0] Fmin=+4bands */ |
||||
0x04 0x7a 0xfd /* [7:5] DCO_FTRNG=+-40% */ |
||||
/* |
||||
* 1.62G CDR settings: |
||||
* [5:2]NOF=64LSB [1:0]DCO scale is 2/5 |
||||
*/ |
||||
0x04 0xc0 0x12 |
||||
0x04 0xc1 0x92 /* Gitune=-37% */ |
||||
0x04 0xc2 0x1c /* Fbstep=100% */ |
||||
0x04 0x32 0x80 /* [7] LOS signal disable */ |
||||
/* RPIO Setting */ |
||||
/* [7:4] LVDS driver bias current 75% (250mV swing) */ |
||||
0x04 0x00 0xb0 |
||||
/* [7:6] Right-bar GPIO output strength is 8mA */ |
||||
0x04 0x15 0x40 |
||||
/* EQ Training State Machine Setting */ |
||||
0x04 0x54 0x10 /* RCO calibration start */ |
||||
/* [4:0] MAX_LANE_COUNT set to one lane */ |
||||
0x01 0x02 0x81 |
||||
/* [4:0] LANE_COUNT_SET set to one lane */ |
||||
0x01 0x21 0x81 |
||||
0x00 0x52 0x20 |
||||
0x00 0xf1 0x03 /* HPD CP toggle enable */ |
||||
0x00 0x62 0x41 |
||||
/* Counter number add 1ms counter delay */ |
||||
0x00 0xf6 0x01 |
||||
/* |
||||
* [6]PWM function control by DPCD0040f[7], default |
||||
* is PWM block always works |
||||
*/ |
||||
0x00 0x77 0x06 |
||||
0x00 0x4c 0x04 |
||||
/* |
||||
* 04h Adjust VTotal tolerance to fix the 30Hz no- |
||||
* display issue |
||||
* DPCD00400='h00 Parade OUI = 'h001cf8 |
||||
*/ |
||||
0x01 0xc0 0x00 |
||||
0x01 0xc1 0x1c /* DPCD00401='h1c */ |
||||
0x01 0xc2 0xf8 /* DPCD00402='hf8 */ |
||||
/* DPCD403~408 = ASCII code D2SLV5='h4432534c5635 */ |
||||
0x01 0xc3 0x44 |
||||
0x01 0xc4 0x32 /* DPCD404 */ |
||||
0x01 0xc5 0x53 /* DPCD405 */ |
||||
0x01 0xc6 0x4c /* DPCD406 */ |
||||
0x01 0xc7 0x56 /* DPCD407 */ |
||||
0x01 0xc8 0x35 /* DPCD408 */ |
||||
/* DPCD40A Initial Code major revision '01' */ |
||||
0x01 0xca 0x01 |
||||
/* DPCD40B Initial Code minor revision '05' */ |
||||
0x01 0xcb 0x05 |
||||
0x01 0xa5 0xa0 /* DPCD720, Select internal PWM */ |
||||
/* |
||||
* 0xff for 100% PWM of brightness, 0h for 0% brightness |
||||
*/ |
||||
0x01 0xa7 0x00 |
||||
/* |
||||
* Set LVDS output as 6bit-VESA mapping, single LVDS |
||||
* channel |
||||
*/ |
||||
0x01 0xcc 0x13 |
||||
0x02 0xb1 0x20 /* Enable SSC set by register */ |
||||
/* Set SSC enabled and +/-1% central spreading */ |
||||
0x04 0x10 0x16 |
||||
0x04 0x59 0x60 /* MPU Clock source: LC => RCO */ |
||||
0x04 0x54 0x14 /* LC -> RCO */ |
||||
0x02 0xa1 0x91>; /* HPD high */ |
||||
}; |
||||
|
||||
soundcodec@20 { |
||||
reg = <0x20>; |
||||
compatible = "maxim,max98088-codec"; |
||||
}; |
||||
}; |
||||
|
||||
#include "cros-ec-keyboard.dtsi" |
@ -0,0 +1,362 @@ |
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <dwc3-uboot.h> |
||||
#include <fdtdec.h> |
||||
#include <asm/io.h> |
||||
#include <errno.h> |
||||
#include <i2c.h> |
||||
#include <mmc.h> |
||||
#include <netdev.h> |
||||
#include <samsung-usb-phy-uboot.h> |
||||
#include <spi.h> |
||||
#include <usb.h> |
||||
#include <video_bridge.h> |
||||
#include <asm/gpio.h> |
||||
#include <asm/arch/cpu.h> |
||||
#include <asm/arch/dwmmc.h> |
||||
#include <asm/arch/mmc.h> |
||||
#include <asm/arch/pinmux.h> |
||||
#include <asm/arch/power.h> |
||||
#include <asm/arch/sromc.h> |
||||
#include <power/pmic.h> |
||||
#include <power/max77686_pmic.h> |
||||
#include <power/regulator.h> |
||||
#include <power/s5m8767.h> |
||||
#include <tmu.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
static void board_enable_audio_codec(void) |
||||
{ |
||||
int node, ret; |
||||
struct gpio_desc en_gpio; |
||||
|
||||
node = fdtdec_next_compatible(gd->fdt_blob, 0, |
||||
COMPAT_SAMSUNG_EXYNOS5_SOUND); |
||||
if (node <= 0) |
||||
return; |
||||
|
||||
ret = gpio_request_by_name_nodev(gd->fdt_blob, node, |
||||
"codec-enable-gpio", 0, &en_gpio, |
||||
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); |
||||
if (ret == -FDT_ERR_NOTFOUND) |
||||
return; |
||||
|
||||
/* Turn on the GPIO which connects to the codec's "enable" line. */ |
||||
gpio_set_pull(gpio_get_number(&en_gpio), S5P_GPIO_PULL_NONE); |
||||
|
||||
#ifdef CONFIG_SOUND_MAX98095 |
||||
/* Enable MAX98095 Codec */ |
||||
gpio_request(EXYNOS5_GPIO_X17, "max98095_enable"); |
||||
gpio_direction_output(EXYNOS5_GPIO_X17, 1); |
||||
gpio_set_pull(EXYNOS5_GPIO_X17, S5P_GPIO_PULL_NONE); |
||||
#endif |
||||
} |
||||
|
||||
int exynos_init(void) |
||||
{ |
||||
board_enable_audio_codec(); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int exynos_set_regulator(const char *name, uint uv) |
||||
{ |
||||
struct udevice *dev; |
||||
int ret; |
||||
|
||||
ret = regulator_get_by_platname(name, &dev); |
||||
if (ret) { |
||||
debug("%s: Cannot find regulator %s\n", __func__, name); |
||||
return ret; |
||||
} |
||||
ret = regulator_set_value(dev, uv); |
||||
if (ret) { |
||||
debug("%s: Cannot set regulator %s\n", __func__, name); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int exynos_power_init(void) |
||||
{ |
||||
struct udevice *dev; |
||||
int ret; |
||||
|
||||
ret = pmic_get("max77686", &dev); |
||||
if (!ret) { |
||||
/* TODO(sjg@chromium.org): Move into the clock/pmic API */ |
||||
ret = pmic_clrsetbits(dev, MAX77686_REG_PMIC_32KHZ, 0, |
||||
MAX77686_32KHCP_EN); |
||||
if (ret) |
||||
return ret; |
||||
ret = pmic_clrsetbits(dev, MAX77686_REG_PMIC_BBAT, 0, |
||||
MAX77686_BBCHOSTEN | MAX77686_BBCVS_3_5V); |
||||
if (ret) |
||||
return ret; |
||||
} else { |
||||
ret = pmic_get("s5m8767-pmic", &dev); |
||||
/* TODO(sjg@chromium.org): Use driver model to access clock */ |
||||
#ifdef CONFIG_PMIC_S5M8767 |
||||
if (!ret) |
||||
s5m8767_enable_32khz_cp(dev); |
||||
#endif |
||||
} |
||||
if (ret == -ENODEV) |
||||
return 0; |
||||
|
||||
ret = regulators_enable_boot_on(false); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
ret = exynos_set_regulator("vdd_mif", 1100000); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
/*
|
||||
* This would normally be 1.3V, but since we are running slowly 1V |
||||
* is enough. For spring it helps reduce CPU temperature and avoid |
||||
* hangs with the case open. |
||||
*/ |
||||
ret = exynos_set_regulator("vdd_arm", 1000000); |
||||
if (ret) |
||||
return ret; |
||||
ret = exynos_set_regulator("vdd_int", 1012500); |
||||
if (ret) |
||||
return ret; |
||||
ret = exynos_set_regulator("vdd_g3d", 1200000); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int board_get_revision(void) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
#ifdef CONFIG_LCD |
||||
|
||||
static int board_dp_bridge_init(struct udevice *dev) |
||||
{ |
||||
const int max_tries = 10; |
||||
int num_tries; |
||||
int ret; |
||||
|
||||
debug("%s\n", __func__); |
||||
ret = video_bridge_attach(dev); |
||||
if (ret) { |
||||
debug("video bridge init failed: %d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
/*
|
||||
* We need to wait for 90ms after bringing up the bridge since there |
||||
* is a phantom "high" on the HPD chip during its bootup. The phantom |
||||
* high comes within 7ms of de-asserting PD and persists for at least |
||||
* 15ms. The real high comes roughly 50ms after PD is de-asserted. The |
||||
* phantom high makes it hard for us to know when the NXP chip is up. |
||||
*/ |
||||
mdelay(90); |
||||
|
||||
for (num_tries = 0; num_tries < max_tries; num_tries++) { |
||||
/* Check HPD. If it's high, or we don't have it, all is well */ |
||||
ret = video_bridge_check_attached(dev); |
||||
if (!ret || ret == -ENOENT) |
||||
return 0; |
||||
|
||||
debug("%s: eDP bridge failed to come up; try %d of %d\n", |
||||
__func__, num_tries, max_tries); |
||||
} |
||||
|
||||
/* Immediately go into bridge reset if the hp line is not high */ |
||||
return -EIO; |
||||
} |
||||
|
||||
static int board_dp_bridge_setup(const void *blob) |
||||
{ |
||||
const int max_tries = 2; |
||||
int num_tries; |
||||
struct udevice *dev; |
||||
int ret; |
||||
|
||||
/* Configure I2C registers for Parade bridge */ |
||||
ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &dev); |
||||
if (ret) { |
||||
debug("video bridge init failed: %d\n", ret); |
||||
return ret; |
||||
} |
||||
|
||||
if (strncmp(dev->driver->name, "parade", 6)) { |
||||
/* Mux HPHPD to the special hotplug detect mode */ |
||||
exynos_pinmux_config(PERIPH_ID_DPHPD, 0); |
||||
} |
||||
|
||||
for (num_tries = 0; num_tries < max_tries; num_tries++) { |
||||
ret = board_dp_bridge_init(dev); |
||||
if (!ret) |
||||
return 0; |
||||
if (num_tries == max_tries - 1) |
||||
break; |
||||
|
||||
/*
|
||||
* If we're here, the bridge chip failed to initialise. |
||||
* Power down the bridge in an attempt to reset. |
||||
*/ |
||||
video_bridge_set_active(dev, false); |
||||
|
||||
/*
|
||||
* Arbitrarily wait 300ms here with DP_N low. Don't know for |
||||
* sure how long we should wait, but we're being paranoid. |
||||
*/ |
||||
mdelay(300); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void exynos_cfg_lcd_gpio(void) |
||||
{ |
||||
/* For Backlight */ |
||||
gpio_request(EXYNOS5_GPIO_B20, "lcd_backlight"); |
||||
gpio_cfg_pin(EXYNOS5_GPIO_B20, S5P_GPIO_OUTPUT); |
||||
gpio_set_value(EXYNOS5_GPIO_B20, 1); |
||||
} |
||||
|
||||
void exynos_set_dp_phy(unsigned int onoff) |
||||
{ |
||||
set_dp_phy_ctrl(onoff); |
||||
} |
||||
|
||||
static int board_dp_set_backlight(int percent) |
||||
{ |
||||
struct udevice *dev; |
||||
int ret; |
||||
|
||||
ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &dev); |
||||
if (!ret) |
||||
ret = video_bridge_set_backlight(dev, percent); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void exynos_backlight_on(unsigned int on) |
||||
{ |
||||
struct udevice *dev; |
||||
int ret; |
||||
|
||||
debug("%s(%u)\n", __func__, on); |
||||
if (!on) |
||||
return; |
||||
|
||||
ret = regulator_get_by_platname("vcd_led", &dev); |
||||
if (!ret) |
||||
ret = regulator_set_enable(dev, true); |
||||
if (ret) |
||||
debug("Failed to enable backlight: ret=%d\n", ret); |
||||
|
||||
/* T5 in the LCD timing spec (defined as > 10ms) */ |
||||
mdelay(10); |
||||
|
||||
/* board_dp_backlight_pwm */ |
||||
gpio_direction_output(EXYNOS5_GPIO_B20, 1); |
||||
|
||||
/* T6 in the LCD timing spec (defined as > 10ms) */ |
||||
mdelay(10); |
||||
|
||||
/* try to set the backlight in the bridge registers */ |
||||
ret = board_dp_set_backlight(80); |
||||
|
||||
/* if we have no bridge or it does not support backlight, use a GPIO */ |
||||
if (ret == -ENODEV || ret == -ENOSYS) { |
||||
gpio_request(EXYNOS5_GPIO_X30, "board_dp_backlight_en"); |
||||
gpio_direction_output(EXYNOS5_GPIO_X30, 1); |
||||
} |
||||
} |
||||
|
||||
void exynos_lcd_power_on(void) |
||||
{ |
||||
struct udevice *dev; |
||||
int ret; |
||||
|
||||
debug("%s\n", __func__); |
||||
ret = regulator_get_by_platname("lcd_vdd", &dev); |
||||
if (!ret) |
||||
ret = regulator_set_enable(dev, true); |
||||
if (ret) |
||||
debug("Failed to enable LCD panel: ret=%d\n", ret); |
||||
|
||||
ret = board_dp_bridge_setup(gd->fdt_blob); |
||||
if (ret && ret != -ENODEV) |
||||
printf("LCD bridge failed to enable: %d\n", ret); |
||||
} |
||||
|
||||
#endif |
||||
|
||||
#ifdef CONFIG_USB_DWC3 |
||||
static struct dwc3_device dwc3_device_data = { |
||||
.maximum_speed = USB_SPEED_SUPER, |
||||
.base = 0x12400000, |
||||
.dr_mode = USB_DR_MODE_PERIPHERAL, |
||||
.index = 0, |
||||
}; |
||||
|
||||
int usb_gadget_handle_interrupts(void) |
||||
{ |
||||
dwc3_uboot_handle_interrupt(0); |
||||
return 0; |
||||
} |
||||
|
||||
int board_usb_init(int index, enum usb_init_type init) |
||||
{ |
||||
struct exynos_usb3_phy *phy = (struct exynos_usb3_phy *) |
||||
samsung_get_base_usb3_phy(); |
||||
|
||||
if (!phy) { |
||||
error("usb3 phy not supported"); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN); |
||||
exynos5_usb3_phy_init(phy); |
||||
|
||||
return dwc3_uboot_init(&dwc3_device_data); |
||||
} |
||||
#endif |
||||
#ifdef CONFIG_SET_DFU_ALT_INFO |
||||
char *get_dfu_alt_system(char *interface, char *devstr) |
||||
{ |
||||
return getenv("dfu_alt_system"); |
||||
} |
||||
|
||||
char *get_dfu_alt_boot(char *interface, char *devstr) |
||||
{ |
||||
struct mmc *mmc; |
||||
char *alt_boot; |
||||
int dev_num; |
||||
|
||||
dev_num = simple_strtoul(devstr, NULL, 10); |
||||
|
||||
mmc = find_mmc_device(dev_num); |
||||
if (!mmc) |
||||
return NULL; |
||||
|
||||
if (mmc_init(mmc)) |
||||
return NULL; |
||||
|
||||
if (IS_SD(mmc)) |
||||
alt_boot = CONFIG_DFU_ALT_BOOT_SD; |
||||
else |
||||
alt_boot = CONFIG_DFU_ALT_BOOT_EMMC; |
||||
|
||||
return alt_boot; |
||||
} |
||||
#endif |
@ -1,306 +0,0 @@ |
||||
/*
|
||||
* Copyright (C) 2012 Samsung Electronics |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fdtdec.h> |
||||
#include <asm/io.h> |
||||
#include <errno.h> |
||||
#include <i2c.h> |
||||
#include <netdev.h> |
||||
#include <spi.h> |
||||
#include <asm/gpio.h> |
||||
#include <asm/arch/cpu.h> |
||||
#include <asm/arch/dwmmc.h> |
||||
#include <asm/arch/mmc.h> |
||||
#include <asm/arch/pinmux.h> |
||||
#include <asm/arch/power.h> |
||||
#include <asm/arch/sromc.h> |
||||
#include <power/pmic.h> |
||||
#include <power/max77686_pmic.h> |
||||
#include <power/tps65090_pmic.h> |
||||
#include <tmu.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
#ifdef CONFIG_SOUND_MAX98095 |
||||
static void board_enable_audio_codec(void) |
||||
{ |
||||
/* Enable MAX98095 Codec */ |
||||
gpio_request(EXYNOS5_GPIO_X17, "max98095_enable"); |
||||
gpio_direction_output(EXYNOS5_GPIO_X17, 1); |
||||
gpio_set_pull(EXYNOS5_GPIO_X17, S5P_GPIO_PULL_NONE); |
||||
} |
||||
#endif |
||||
|
||||
int exynos_init(void) |
||||
{ |
||||
#ifdef CONFIG_SOUND_MAX98095 |
||||
board_enable_audio_codec(); |
||||
#endif |
||||
return 0; |
||||
} |
||||
|
||||
#if defined(CONFIG_POWER) |
||||
#ifdef CONFIG_POWER_MAX77686 |
||||
static int pmic_reg_update(struct pmic *p, int reg, uint regval) |
||||
{ |
||||
u32 val; |
||||
int ret = 0; |
||||
|
||||
ret = pmic_reg_read(p, reg, &val); |
||||
if (ret) { |
||||
debug("%s: PMIC %d register read failed\n", __func__, reg); |
||||
return -1; |
||||
} |
||||
val |= regval; |
||||
ret = pmic_reg_write(p, reg, val); |
||||
if (ret) { |
||||
debug("%s: PMIC %d register write failed\n", __func__, reg); |
||||
return -1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static int max77686_init(void) |
||||
{ |
||||
struct pmic *p; |
||||
|
||||
if (pmic_init(I2C_PMIC)) |
||||
return -1; |
||||
|
||||
p = pmic_get("MAX77686_PMIC"); |
||||
if (!p) |
||||
return -ENODEV; |
||||
|
||||
if (pmic_probe(p)) |
||||
return -1; |
||||
|
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_32KHZ, MAX77686_32KHCP_EN)) |
||||
return -1; |
||||
|
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_BBAT, |
||||
MAX77686_BBCHOSTEN | MAX77686_BBCVS_3_5V)) |
||||
return -1; |
||||
|
||||
/* VDD_MIF */ |
||||
if (pmic_reg_write(p, MAX77686_REG_PMIC_BUCK1OUT, |
||||
MAX77686_BUCK1OUT_1V)) { |
||||
debug("%s: PMIC %d register write failed\n", __func__, |
||||
MAX77686_REG_PMIC_BUCK1OUT); |
||||
return -1; |
||||
} |
||||
|
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_BUCK1CRTL, |
||||
MAX77686_BUCK1CTRL_EN)) |
||||
return -1; |
||||
|
||||
/* VDD_ARM */ |
||||
if (pmic_reg_write(p, MAX77686_REG_PMIC_BUCK2DVS1, |
||||
MAX77686_BUCK2DVS1_1_3V)) { |
||||
debug("%s: PMIC %d register write failed\n", __func__, |
||||
MAX77686_REG_PMIC_BUCK2DVS1); |
||||
return -1; |
||||
} |
||||
|
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_BUCK2CTRL1, |
||||
MAX77686_BUCK2CTRL_ON)) |
||||
return -1; |
||||
|
||||
/* VDD_INT */ |
||||
if (pmic_reg_write(p, MAX77686_REG_PMIC_BUCK3DVS1, |
||||
MAX77686_BUCK3DVS1_1_0125V)) { |
||||
debug("%s: PMIC %d register write failed\n", __func__, |
||||
MAX77686_REG_PMIC_BUCK3DVS1); |
||||
return -1; |
||||
} |
||||
|
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_BUCK3CTRL, |
||||
MAX77686_BUCK3CTRL_ON)) |
||||
return -1; |
||||
|
||||
/* VDD_G3D */ |
||||
if (pmic_reg_write(p, MAX77686_REG_PMIC_BUCK4DVS1, |
||||
MAX77686_BUCK4DVS1_1_2V)) { |
||||
debug("%s: PMIC %d register write failed\n", __func__, |
||||
MAX77686_REG_PMIC_BUCK4DVS1); |
||||
return -1; |
||||
} |
||||
|
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_BUCK4CTRL1, |
||||
MAX77686_BUCK3CTRL_ON)) |
||||
return -1; |
||||
|
||||
/* VDD_LDO2 */ |
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_LDO2CTRL1, |
||||
MAX77686_LD02CTRL1_1_5V | EN_LDO)) |
||||
return -1; |
||||
|
||||
/* VDD_LDO3 */ |
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_LDO3CTRL1, |
||||
MAX77686_LD03CTRL1_1_8V | EN_LDO)) |
||||
return -1; |
||||
|
||||
/* VDD_LDO5 */ |
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_LDO5CTRL1, |
||||
MAX77686_LD05CTRL1_1_8V | EN_LDO)) |
||||
return -1; |
||||
|
||||
/* VDD_LDO10 */ |
||||
if (pmic_reg_update(p, MAX77686_REG_PMIC_LDO10CTRL1, |
||||
MAX77686_LD10CTRL1_1_8V | EN_LDO)) |
||||
return -1; |
||||
|
||||
return 0; |
||||
} |
||||
#endif /* CONFIG_POWER_MAX77686 */ |
||||
|
||||
int exynos_power_init(void) |
||||
{ |
||||
int ret = 0; |
||||
|
||||
#ifdef CONFIG_POWER_MAX77686 |
||||
ret = max77686_init(); |
||||
if (ret) |
||||
return ret; |
||||
#endif |
||||
#ifdef CONFIG_POWER_TPS65090 |
||||
/*
|
||||
* The TPS65090 may not be in the device tree. If so, it is not |
||||
* an error. |
||||
*/ |
||||
ret = tps65090_init(); |
||||
if (ret == 0 || ret == -ENODEV) |
||||
return 0; |
||||
#endif |
||||
|
||||
return ret; |
||||
} |
||||
#endif /* CONFIG_POWER */ |
||||
|
||||
#ifdef CONFIG_LCD |
||||
static int board_dp_bridge_setup(void) |
||||
{ |
||||
const int max_tries = 10; |
||||
int num_tries, node; |
||||
|
||||
/*
|
||||
* TODO(sjg): Use device tree for GPIOs when exynos GPIO |
||||
* numbering patch is in mainline. |
||||
*/ |
||||
debug("%s\n", __func__); |
||||
node = fdtdec_next_compatible(gd->fdt_blob, 0, COMPAT_NXP_PTN3460); |
||||
if (node < 0) { |
||||
debug("%s: No node for DP bridge in device tree\n", __func__); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
/* Setup the GPIOs */ |
||||
|
||||
/* PD is ACTIVE_LOW, and initially de-asserted */ |
||||
gpio_request(EXYNOS5_GPIO_Y25, "dp_bridge_pd"); |
||||
gpio_set_pull(EXYNOS5_GPIO_Y25, S5P_GPIO_PULL_NONE); |
||||
gpio_direction_output(EXYNOS5_GPIO_Y25, 1); |
||||
|
||||
/* Reset is ACTIVE_LOW */ |
||||
gpio_request(EXYNOS5_GPIO_X15, "dp_bridge_reset"); |
||||
gpio_set_pull(EXYNOS5_GPIO_X15, S5P_GPIO_PULL_NONE); |
||||
gpio_direction_output(EXYNOS5_GPIO_X15, 0); |
||||
|
||||
udelay(10); |
||||
gpio_set_value(EXYNOS5_GPIO_X15, 1); |
||||
|
||||
gpio_request(EXYNOS5_GPIO_X07, "dp_bridge_hpd"); |
||||
gpio_direction_input(EXYNOS5_GPIO_X07); |
||||
|
||||
/*
|
||||
* We need to wait for 90ms after bringing up the bridge since there |
||||
* is a phantom "high" on the HPD chip during its bootup. The phantom |
||||
* high comes within 7ms of de-asserting PD and persists for at least |
||||
* 15ms. The real high comes roughly 50ms after PD is de-asserted. The |
||||
* phantom high makes it hard for us to know when the NXP chip is up. |
||||
*/ |
||||
mdelay(90); |
||||
|
||||
for (num_tries = 0; num_tries < max_tries; num_tries++) { |
||||
/* Check HPD. If it's high, we're all good. */ |
||||
if (gpio_get_value(EXYNOS5_GPIO_X07)) |
||||
return 0; |
||||
|
||||
debug("%s: eDP bridge failed to come up; try %d of %d\n", |
||||
__func__, num_tries, max_tries); |
||||
} |
||||
|
||||
/* Immediately go into bridge reset if the hp line is not high */ |
||||
return -ENODEV; |
||||
} |
||||
|
||||
void exynos_cfg_lcd_gpio(void) |
||||
{ |
||||
/* For Backlight */ |
||||
gpio_request(EXYNOS5_GPIO_B20, "lcd_backlight"); |
||||
gpio_cfg_pin(EXYNOS5_GPIO_B20, S5P_GPIO_OUTPUT); |
||||
gpio_set_value(EXYNOS5_GPIO_B20, 1); |
||||
|
||||
/* LCD power on */ |
||||
gpio_request(EXYNOS5_GPIO_X15, "lcd_power"); |
||||
gpio_cfg_pin(EXYNOS5_GPIO_X15, S5P_GPIO_OUTPUT); |
||||
gpio_set_value(EXYNOS5_GPIO_X15, 1); |
||||
|
||||
/* Set Hotplug detect for DP */ |
||||
gpio_cfg_pin(EXYNOS5_GPIO_X07, S5P_GPIO_FUNC(0x3)); |
||||
} |
||||
|
||||
void exynos_set_dp_phy(unsigned int onoff) |
||||
{ |
||||
set_dp_phy_ctrl(onoff); |
||||
} |
||||
|
||||
void exynos_backlight_on(unsigned int on) |
||||
{ |
||||
debug("%s(%u)\n", __func__, on); |
||||
|
||||
if (!on) |
||||
return; |
||||
|
||||
#ifdef CONFIG_POWER_TPS65090 |
||||
int ret; |
||||
|
||||
ret = tps65090_fet_enable(1); /* Enable FET1, backlight */ |
||||
if (ret) |
||||
return; |
||||
|
||||
/* T5 in the LCD timing spec (defined as > 10ms) */ |
||||
mdelay(10); |
||||
|
||||
/* board_dp_backlight_pwm */ |
||||
gpio_direction_output(EXYNOS5_GPIO_B20, 1); |
||||
|
||||
/* T6 in the LCD timing spec (defined as > 10ms) */ |
||||
mdelay(10); |
||||
|
||||
/* board_dp_backlight_en */ |
||||
gpio_request(EXYNOS5_GPIO_X30, "board_dp_backlight_en"); |
||||
gpio_direction_output(EXYNOS5_GPIO_X30, 1); |
||||
#endif |
||||
} |
||||
|
||||
void exynos_lcd_power_on(void) |
||||
{ |
||||
int ret; |
||||
|
||||
debug("%s\n", __func__); |
||||
|
||||
#ifdef CONFIG_POWER_TPS65090 |
||||
/* board_dp_lcd_vdd */ |
||||
tps65090_fet_enable(6); /* Enable FET6, lcd panel */ |
||||
#endif |
||||
|
||||
ret = board_dp_bridge_setup(); |
||||
if (ret && ret != -ENODEV) |
||||
printf("LCD bridge failed to enable: %d\n", ret); |
||||
} |
||||
|
||||
#endif |
@ -1,143 +0,0 @@ |
||||
/*
|
||||
* Copyright (C) 2013 Samsung Electronics |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fdtdec.h> |
||||
#include <errno.h> |
||||
#include <asm/io.h> |
||||
#include <asm/gpio.h> |
||||
#include <asm/arch/cpu.h> |
||||
#include <asm/arch/board.h> |
||||
#include <asm/arch/power.h> |
||||
#include <asm/arch/system.h> |
||||
#include <asm/arch/pinmux.h> |
||||
#include <asm/arch/dp_info.h> |
||||
#include <asm/arch/xhci-exynos.h> |
||||
#include <power/tps65090_pmic.h> |
||||
#include <i2c.h> |
||||
#include <lcd.h> |
||||
#include <mmc.h> |
||||
#include <parade.h> |
||||
#include <spi.h> |
||||
#include <usb.h> |
||||
#include <dwc3-uboot.h> |
||||
#include <samsung-usb-phy-uboot.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
int exynos_init(void) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
#ifdef CONFIG_LCD |
||||
static int has_edp_bridge(void) |
||||
{ |
||||
int node; |
||||
|
||||
node = fdtdec_next_compatible(gd->fdt_blob, 0, COMPAT_PARADE_PS8625); |
||||
|
||||
/* No node for bridge in device tree. */ |
||||
if (node <= 0) |
||||
return 0; |
||||
|
||||
/* Default is with bridge ic */ |
||||
return 1; |
||||
} |
||||
|
||||
void exynos_lcd_power_on(void) |
||||
{ |
||||
int ret; |
||||
|
||||
#ifdef CONFIG_POWER_TPS65090 |
||||
ret = tps65090_init(); |
||||
if (ret < 0) { |
||||
printf("%s: tps65090_init() failed\n", __func__); |
||||
return; |
||||
} |
||||
|
||||
tps65090_fet_enable(6); |
||||
#endif |
||||
|
||||
mdelay(5); |
||||
|
||||
if (has_edp_bridge()) |
||||
if (parade_init(gd->fdt_blob)) |
||||
printf("%s: ps8625_init() failed\n", __func__); |
||||
} |
||||
|
||||
void exynos_backlight_on(unsigned int onoff) |
||||
{ |
||||
#ifdef CONFIG_POWER_TPS65090 |
||||
tps65090_fet_enable(1); |
||||
#endif |
||||
} |
||||
#endif |
||||
|
||||
int board_get_revision(void) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
#ifdef CONFIG_USB_DWC3 |
||||
static struct dwc3_device dwc3_device_data = { |
||||
.maximum_speed = USB_SPEED_SUPER, |
||||
.base = 0x12400000, |
||||
.dr_mode = USB_DR_MODE_PERIPHERAL, |
||||
.index = 0, |
||||
}; |
||||
|
||||
int usb_gadget_handle_interrupts(void) |
||||
{ |
||||
dwc3_uboot_handle_interrupt(0); |
||||
return 0; |
||||
} |
||||
|
||||
int board_usb_init(int index, enum usb_init_type init) |
||||
{ |
||||
struct exynos_usb3_phy *phy = (struct exynos_usb3_phy *) |
||||
samsung_get_base_usb3_phy(); |
||||
|
||||
if (!phy) { |
||||
error("usb3 phy not supported"); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN); |
||||
exynos5_usb3_phy_init(phy); |
||||
|
||||
return dwc3_uboot_init(&dwc3_device_data); |
||||
} |
||||
#endif |
||||
#ifdef CONFIG_SET_DFU_ALT_INFO |
||||
char *get_dfu_alt_system(char *interface, char *devstr) |
||||
{ |
||||
return getenv("dfu_alt_system"); |
||||
} |
||||
|
||||
char *get_dfu_alt_boot(char *interface, char *devstr) |
||||
{ |
||||
struct mmc *mmc; |
||||
char *alt_boot; |
||||
int dev_num; |
||||
|
||||
dev_num = simple_strtoul(devstr, NULL, 10); |
||||
|
||||
mmc = find_mmc_device(dev_num); |
||||
if (!mmc) |
||||
return NULL; |
||||
|
||||
if (mmc_init(mmc)) |
||||
return NULL; |
||||
|
||||
if (IS_SD(mmc)) |
||||
alt_boot = CONFIG_DFU_ALT_BOOT_SD; |
||||
else |
||||
alt_boot = CONFIG_DFU_ALT_BOOT_EMMC; |
||||
|
||||
return alt_boot; |
||||
} |
||||
#endif |
@ -0,0 +1,42 @@ |
||||
CONFIG_ARM=y |
||||
CONFIG_ARCH_EXYNOS=y |
||||
CONFIG_TARGET_SPRING=y |
||||
CONFIG_DEFAULT_DEVICE_TREE="exynos5250-spring" |
||||
CONFIG_SPL=y |
||||
# CONFIG_CMD_IMLS is not set |
||||
# CONFIG_CMD_SETEXPR is not set |
||||
CONFIG_CMD_SOUND=y |
||||
CONFIG_SPI_FLASH=y |
||||
CONFIG_CMD_CROS_EC=y |
||||
CONFIG_CROS_EC=y |
||||
CONFIG_CROS_EC_I2C=y |
||||
CONFIG_CROS_EC_KEYB=y |
||||
CONFIG_SOUND=y |
||||
CONFIG_I2S=y |
||||
CONFIG_I2S_SAMSUNG=y |
||||
CONFIG_SOUND_MAX98095=y |
||||
CONFIG_SOUND_WM8994=y |
||||
CONFIG_USB=y |
||||
CONFIG_DM_USB=y |
||||
CONFIG_DM_I2C=y |
||||
CONFIG_DM_PMIC=y |
||||
CONFIG_DM_REGULATOR=y |
||||
CONFIG_PMIC_TPS65090=y |
||||
CONFIG_REGULATOR_TPS65090=y |
||||
CONFIG_DM_I2C_COMPAT=y |
||||
CONFIG_I2C_ARB_GPIO_CHALLENGE=y |
||||
CONFIG_I2C_MUX=y |
||||
CONFIG_CMD_PMIC=y |
||||
CONFIG_CMD_REGULATOR=y |
||||
CONFIG_ERRNO_STR=y |
||||
CONFIG_DM_PMIC_MAX77686=y |
||||
CONFIG_DM_REGULATOR_MAX77686=y |
||||
CONFIG_DEBUG_UART=y |
||||
CONFIG_DEBUG_UART_S5P=y |
||||
CONFIG_DEBUG_UART_CLOCK=100000000 |
||||
CONFIG_DEBUG_UART_BASE=0x12c30000 |
||||
CONFIG_I2C_CROS_EC_LDO=y |
||||
CONFIG_PMIC_S5M8767=y |
||||
CONFIG_REGULATOR_S5M8767=y |
||||
CONFIG_VIDEO_BRIDGE=y |
||||
CONFIG_VIDEO_BRIDGE_PARADE_PS862X=y |
@ -0,0 +1,60 @@ |
||||
I2C Bus Arbitration |
||||
=================== |
||||
|
||||
While I2C supports multi-master buses this is difficult to get right. |
||||
The implementation on the master side in software is quite complex. |
||||
Clock-stretching and the arbitrary time that an I2C transaction can take |
||||
make it difficult to share the bus fairly in the face of high traffic. |
||||
When one or more masters can be reset independently part-way through a |
||||
transaction it is hard to know the state of the bus. |
||||
|
||||
U-Boot provides a scheme based on two 'claim' GPIOs, one driven by the |
||||
AP (Application Processor, meaning the main CPU) and one driven by the EC |
||||
(Embedded Controller, a small CPU aimed at handling system tasks). With |
||||
these they can communicate and reliably share the bus. This scheme has |
||||
minimal overhead and involves very little code. The scheme can survive |
||||
reboots by either side without difficulty. |
||||
|
||||
Since U-Boot runs on the AP, the terminology used is 'our' claim GPIO, |
||||
meaning the AP's, and 'their' claim GPIO, meaning the EC's. This terminology |
||||
is used by the device tree bindings in Linux also. |
||||
|
||||
The driver is implemented as an I2C mux, as it is in Linux. See |
||||
i2c-arb-gpio-challenge for the implementation. |
||||
|
||||
GPIO lines are shared between the AP and EC to manage the bus. The AP and EC |
||||
each have a 'bus claim' line, which is an output that the other can see. |
||||
|
||||
- AP_CLAIM: output from AP, signalling to the EC that the AP wants the bus |
||||
- EC_CLAIM: output from EC, signalling to the AP that the EC wants the bus |
||||
|
||||
The basic algorithm is to assert your line when you want the bus, then make |
||||
sure that the other side doesn't want it also. A detailed explanation is best |
||||
done with an example. |
||||
|
||||
Let's say the AP wants to claim the bus. It: |
||||
|
||||
1. Asserts AP_CLAIM |
||||
2. Waits a little bit for the other side to notice (slew time) |
||||
3. Checks EC_CLAIM. If this is not asserted, then the AP has the bus, and we |
||||
are done |
||||
4. Otherwise, wait for a few milliseconds (retry time) and see if EC_CLAIM is |
||||
released |
||||
5. If not, back off, release the claim and wait for a few more milliseconds |
||||
(retry time again) |
||||
6. Go back to 1 if things don't look wedged (wait time has expired) |
||||
7. Panic. The other side is hung with the CLAIM line set. |
||||
|
||||
The same algorithm applies on the EC. |
||||
|
||||
To release the bus, just de-assert the claim line. |
||||
|
||||
Typical delays are: |
||||
- slew time 10 us |
||||
- retry time 3 ms |
||||
- wait time - 50ms |
||||
|
||||
In general the traffic is fairly light, and in particular the EC wants access |
||||
to the bus quite rarely (maybe every 10s or 30s to check the battery). This |
||||
scheme works very nicely with very low contention. There is only a 10 us |
||||
wait for access to the bus assuming that the other side isn't using it. |
@ -0,0 +1,60 @@ |
||||
Common i2c bus multiplexer/switch properties. |
||||
|
||||
An i2c bus multiplexer/switch will have several child busses that are |
||||
numbered uniquely in a device dependent manner. The nodes for an i2c bus |
||||
multiplexer/switch will have one child node for each child |
||||
bus. |
||||
|
||||
Required properties: |
||||
- #address-cells = <1>; |
||||
- #size-cells = <0>; |
||||
|
||||
Required properties for child nodes: |
||||
- #address-cells = <1>; |
||||
- #size-cells = <0>; |
||||
- reg : The sub-bus number. |
||||
|
||||
Optional properties for child nodes: |
||||
- Other properties specific to the multiplexer/switch hardware. |
||||
- Child nodes conforming to i2c bus binding |
||||
|
||||
|
||||
Example : |
||||
|
||||
/* |
||||
An NXP pca9548 8 channel I2C multiplexer at address 0x70 |
||||
with two NXP pca8574 GPIO expanders attached, one each to |
||||
ports 3 and 4. |
||||
*/ |
||||
|
||||
mux@70 { |
||||
compatible = "nxp,pca9548"; |
||||
reg = <0x70>; |
||||
#address-cells = <1>; |
||||
#size-cells = <0>; |
||||
|
||||
i2c@3 { |
||||
#address-cells = <1>; |
||||
#size-cells = <0>; |
||||
reg = <3>; |
||||
|
||||
gpio1: gpio@38 { |
||||
compatible = "nxp,pca8574"; |
||||
reg = <0x38>; |
||||
#gpio-cells = <2>; |
||||
gpio-controller; |
||||
}; |
||||
}; |
||||
i2c@4 { |
||||
#address-cells = <1>; |
||||
#size-cells = <0>; |
||||
reg = <4>; |
||||
|
||||
gpio2: gpio@38 { |
||||
compatible = "nxp,pca8574"; |
||||
reg = <0x38>; |
||||
#gpio-cells = <2>; |
||||
gpio-controller; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,33 @@ |
||||
ps8622-bridge bindings |
||||
|
||||
Required properties: |
||||
- compatible: "parade,ps8622" or "parade,ps8625" |
||||
- reg: first i2c address of the bridge |
||||
- sleep-gpios: OF device-tree gpio specification for PD_ pin. |
||||
- reset-gpios: OF device-tree gpio specification for RST_ pin. |
||||
- parade,regs: List of 3-byte registers tuples to write: |
||||
<I2C chip address offset> <register> <value> |
||||
|
||||
Optional properties: |
||||
- lane-count: number of DP lanes to use |
||||
- use-external-pwm: backlight will be controlled by an external PWM |
||||
- video interfaces: Device node can contain video interface port |
||||
nodes for panel according to [1]. |
||||
|
||||
[1]: Documentation/devicetree/bindings/media/video-interfaces.txt |
||||
|
||||
Example: |
||||
lvds-bridge@48 { |
||||
compatible = "parade,ps8622"; |
||||
reg = <0x48>; |
||||
sleep-gpios = <&gpc3 6 1 0 0>; |
||||
reset-gpios = <&gpc3 1 1 0 0>; |
||||
lane-count = <1>; |
||||
ports { |
||||
port@0 { |
||||
bridge_out: endpoint { |
||||
remote-endpoint = <&panel_in>; |
||||
}; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,259 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com> |
||||
* |
||||
* Based on the original work in Linux by |
||||
* Copyright (c) 2006 SUSE Linux Products GmbH |
||||
* Copyright (c) 2006 Tejun Heo <teheo@suse.de> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <linux/compat.h> |
||||
#include <linux/kernel.h> |
||||
#include <linux/list.h> |
||||
#include <dm/device.h> |
||||
#include <dm/root.h> |
||||
#include <dm/util.h> |
||||
|
||||
/**
|
||||
* struct devres - Bookkeeping info for managed device resource |
||||
* @entry: List to associate this structure with a device |
||||
* @release: Callback invoked when this resource is released |
||||
* @probe: Flag to show when this resource was allocated |
||||
(true = probe, false = bind) |
||||
* @name: Name of release function |
||||
* @size: Size of resource data |
||||
* @data: Resource data |
||||
*/ |
||||
struct devres { |
||||
struct list_head entry; |
||||
dr_release_t release; |
||||
bool probe; |
||||
#ifdef CONFIG_DEBUG_DEVRES |
||||
const char *name; |
||||
size_t size; |
||||
#endif |
||||
unsigned long long data[]; |
||||
}; |
||||
|
||||
#ifdef CONFIG_DEBUG_DEVRES |
||||
static void set_node_dbginfo(struct devres *dr, const char *name, size_t size) |
||||
{ |
||||
dr->name = name; |
||||
dr->size = size; |
||||
} |
||||
|
||||
static void devres_log(struct udevice *dev, struct devres *dr, |
||||
const char *op) |
||||
{ |
||||
printf("%s: DEVRES %3s %p %s (%lu bytes)\n", |
||||
dev->name, op, dr, dr->name, (unsigned long)dr->size); |
||||
} |
||||
#else /* CONFIG_DEBUG_DEVRES */ |
||||
#define set_node_dbginfo(dr, n, s) do {} while (0) |
||||
#define devres_log(dev, dr, op) do {} while (0) |
||||
#endif |
||||
|
||||
#if CONFIG_DEBUG_DEVRES |
||||
void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp, |
||||
const char *name) |
||||
#else |
||||
void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp) |
||||
#endif |
||||
{ |
||||
size_t tot_size = sizeof(struct devres) + size; |
||||
struct devres *dr; |
||||
|
||||
dr = kmalloc(tot_size, gfp); |
||||
if (unlikely(!dr)) |
||||
return NULL; |
||||
|
||||
INIT_LIST_HEAD(&dr->entry); |
||||
dr->release = release; |
||||
set_node_dbginfo(dr, name, size); |
||||
|
||||
return dr->data; |
||||
} |
||||
|
||||
void devres_free(void *res) |
||||
{ |
||||
if (res) { |
||||
struct devres *dr = container_of(res, struct devres, data); |
||||
|
||||
BUG_ON(!list_empty(&dr->entry)); |
||||
kfree(dr); |
||||
} |
||||
} |
||||
|
||||
void devres_add(struct udevice *dev, void *res) |
||||
{ |
||||
struct devres *dr = container_of(res, struct devres, data); |
||||
|
||||
devres_log(dev, dr, "ADD"); |
||||
BUG_ON(!list_empty(&dr->entry)); |
||||
dr->probe = dev->flags & DM_FLAG_BOUND ? true : false; |
||||
list_add_tail(&dr->entry, &dev->devres_head); |
||||
} |
||||
|
||||
void *devres_find(struct udevice *dev, dr_release_t release, |
||||
dr_match_t match, void *match_data) |
||||
{ |
||||
struct devres *dr; |
||||
|
||||
list_for_each_entry_reverse(dr, &dev->devres_head, entry) { |
||||
if (dr->release != release) |
||||
continue; |
||||
if (match && !match(dev, dr->data, match_data)) |
||||
continue; |
||||
return dr->data; |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
|
||||
void *devres_get(struct udevice *dev, void *new_res, |
||||
dr_match_t match, void *match_data) |
||||
{ |
||||
struct devres *new_dr = container_of(new_res, struct devres, data); |
||||
void *res; |
||||
|
||||
res = devres_find(dev, new_dr->release, match, match_data); |
||||
if (!res) { |
||||
devres_add(dev, new_res); |
||||
res = new_res; |
||||
new_res = NULL; |
||||
} |
||||
devres_free(new_res); |
||||
|
||||
return res; |
||||
} |
||||
|
||||
void *devres_remove(struct udevice *dev, dr_release_t release, |
||||
dr_match_t match, void *match_data) |
||||
{ |
||||
void *res; |
||||
|
||||
res = devres_find(dev, release, match, match_data); |
||||
if (res) { |
||||
struct devres *dr = container_of(res, struct devres, data); |
||||
|
||||
list_del_init(&dr->entry); |
||||
devres_log(dev, dr, "REM"); |
||||
} |
||||
|
||||
return res; |
||||
} |
||||
|
||||
int devres_destroy(struct udevice *dev, dr_release_t release, |
||||
dr_match_t match, void *match_data) |
||||
{ |
||||
void *res; |
||||
|
||||
res = devres_remove(dev, release, match, match_data); |
||||
if (unlikely(!res)) |
||||
return -ENOENT; |
||||
|
||||
devres_free(res); |
||||
return 0; |
||||
} |
||||
|
||||
int devres_release(struct udevice *dev, dr_release_t release, |
||||
dr_match_t match, void *match_data) |
||||
{ |
||||
void *res; |
||||
|
||||
res = devres_remove(dev, release, match, match_data); |
||||
if (unlikely(!res)) |
||||
return -ENOENT; |
||||
|
||||
(*release)(dev, res); |
||||
devres_free(res); |
||||
return 0; |
||||
} |
||||
|
||||
static void release_nodes(struct udevice *dev, struct list_head *head, |
||||
bool probe_only) |
||||
{ |
||||
struct devres *dr, *tmp; |
||||
|
||||
list_for_each_entry_safe_reverse(dr, tmp, head, entry) { |
||||
if (probe_only && !dr->probe) |
||||
break; |
||||
devres_log(dev, dr, "REL"); |
||||
dr->release(dev, dr->data); |
||||
list_del(&dr->entry); |
||||
kfree(dr); |
||||
} |
||||
} |
||||
|
||||
void devres_release_probe(struct udevice *dev) |
||||
{ |
||||
release_nodes(dev, &dev->devres_head, true); |
||||
} |
||||
|
||||
void devres_release_all(struct udevice *dev) |
||||
{ |
||||
release_nodes(dev, &dev->devres_head, false); |
||||
} |
||||
|
||||
#ifdef CONFIG_DEBUG_DEVRES |
||||
static void dump_resources(struct udevice *dev, int depth) |
||||
{ |
||||
struct devres *dr; |
||||
struct udevice *child; |
||||
|
||||
printf("- %s\n", dev->name); |
||||
|
||||
list_for_each_entry(dr, &dev->devres_head, entry) |
||||
printf(" %p (%lu byte) %s %s\n", dr, |
||||
(unsigned long)dr->size, dr->name, |
||||
dr->probe ? "PROBE" : "BIND"); |
||||
|
||||
list_for_each_entry(child, &dev->child_head, sibling_node) |
||||
dump_resources(child, depth + 1); |
||||
} |
||||
|
||||
void dm_dump_devres(void) |
||||
{ |
||||
struct udevice *root; |
||||
|
||||
root = dm_root(); |
||||
if (root) |
||||
dump_resources(root, 0); |
||||
} |
||||
#endif |
||||
|
||||
/*
|
||||
* Managed kmalloc/kfree |
||||
*/ |
||||
static void devm_kmalloc_release(struct udevice *dev, void *res) |
||||
{ |
||||
/* noop */ |
||||
} |
||||
|
||||
static int devm_kmalloc_match(struct udevice *dev, void *res, void *data) |
||||
{ |
||||
return res == data; |
||||
} |
||||
|
||||
void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp) |
||||
{ |
||||
void *data; |
||||
|
||||
data = _devres_alloc(devm_kmalloc_release, size, gfp); |
||||
if (unlikely(!data)) |
||||
return NULL; |
||||
|
||||
devres_add(dev, data); |
||||
|
||||
return data; |
||||
} |
||||
|
||||
void devm_kfree(struct udevice *dev, void *p) |
||||
{ |
||||
int rc; |
||||
|
||||
rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p); |
||||
WARN_ON(rc); |
||||
} |
@ -0,0 +1,77 @@ |
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <cros_ec.h> |
||||
#include <errno.h> |
||||
#include <i2c.h> |
||||
#include <power/tps65090.h> |
||||
|
||||
static int cros_ec_ldo_set_bus_speed(struct udevice *dev, unsigned int speed) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
static int cros_ec_ldo_xfer(struct udevice *dev, struct i2c_msg *msg, |
||||
int nmsgs) |
||||
{ |
||||
bool is_read = nmsgs > 1; |
||||
int fet_id, ret; |
||||
|
||||
/*
|
||||
* Look for reads and writes of the LDO registers. In either case the |
||||
* first message is a write with the register number as the first byte. |
||||
*/ |
||||
if (!nmsgs || !msg->len || (msg->flags & I2C_M_RD)) { |
||||
debug("%s: Invalid message\n", __func__); |
||||
goto err; |
||||
} |
||||
|
||||
fet_id = msg->buf[0] - REG_FET_BASE; |
||||
if (fet_id < 1 || fet_id > MAX_FET_NUM) { |
||||
debug("%s: Invalid FET %d\n", __func__, fet_id); |
||||
goto err; |
||||
} |
||||
|
||||
if (is_read) { |
||||
uint8_t state; |
||||
|
||||
ret = cros_ec_get_ldo(dev->parent, fet_id, &state); |
||||
if (!ret) |
||||
msg[1].buf[0] = state ? |
||||
FET_CTRL_ENFET | FET_CTRL_PGFET : 0; |
||||
} else { |
||||
bool on = msg->buf[1] & FET_CTRL_ENFET; |
||||
|
||||
ret = cros_ec_set_ldo(dev->parent, fet_id, on); |
||||
} |
||||
|
||||
return ret; |
||||
|
||||
err: |
||||
/* Indicate that the message is unimplemented */ |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
static const struct dm_i2c_ops cros_ec_i2c_ops = { |
||||
.xfer = cros_ec_ldo_xfer, |
||||
.set_bus_speed = cros_ec_ldo_set_bus_speed, |
||||
}; |
||||
|
||||
static const struct udevice_id cros_ec_i2c_ids[] = { |
||||
{ .compatible = "google,cros-ec-ldo-tunnel" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(cros_ec_ldo) = { |
||||
.name = "cros_ec_ldo_tunnel", |
||||
.id = UCLASS_I2C, |
||||
.of_match = cros_ec_i2c_ids, |
||||
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), |
||||
.ops = &cros_ec_i2c_ops, |
||||
}; |
@ -0,0 +1,41 @@ |
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <cros_ec.h> |
||||
#include <errno.h> |
||||
#include <i2c.h> |
||||
|
||||
static int cros_ec_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
static int cros_ec_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, |
||||
int nmsgs) |
||||
{ |
||||
return cros_ec_i2c_tunnel(dev->parent, msg, nmsgs); |
||||
} |
||||
|
||||
static const struct dm_i2c_ops cros_ec_i2c_ops = { |
||||
.xfer = cros_ec_i2c_xfer, |
||||
.set_bus_speed = cros_ec_i2c_set_bus_speed, |
||||
}; |
||||
|
||||
static const struct udevice_id cros_ec_i2c_ids[] = { |
||||
{ .compatible = "google,cros-ec-i2c-tunnel" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(cros_ec_tunnel) = { |
||||
.name = "cros_ec_tunnel", |
||||
.id = UCLASS_I2C, |
||||
.of_match = cros_ec_i2c_ids, |
||||
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), |
||||
.ops = &cros_ec_i2c_ops, |
||||
}; |
@ -0,0 +1,17 @@ |
||||
config I2C_MUX |
||||
bool "Suport I2C multiplexers" |
||||
depends on DM_I2C |
||||
help |
||||
This enables I2C buses to be multiplexed, so that you can select |
||||
one of several buses using some sort of control mechanism. The |
||||
bus select is handled automatically when that bus is accessed, |
||||
using a suitable I2C MUX driver. |
||||
|
||||
config I2C_ARB_GPIO_CHALLENGE |
||||
bool "GPIO-based I2C arbitration" |
||||
depends on I2C_MUX |
||||
help |
||||
If you say yes to this option, support will be included for an |
||||
I2C multimaster arbitration scheme using GPIOs and a challenge & |
||||
response mechanism where masters have to claim the bus by asserting |
||||
a GPIO. |
@ -0,0 +1,7 @@ |
||||
#
|
||||
# Copyright (c) 2015 Google, Inc
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
|
||||
obj-$(CONFIG_I2C_MUX) += i2c-mux-uclass.o
|
@ -0,0 +1,147 @@ |
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <errno.h> |
||||
#include <i2c.h> |
||||
#include <asm/gpio.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
struct i2c_arbitrator_priv { |
||||
struct gpio_desc ap_claim; |
||||
struct gpio_desc ec_claim; |
||||
uint slew_delay_us; |
||||
uint wait_retry_ms; |
||||
uint wait_free_ms; |
||||
}; |
||||
|
||||
int i2c_arbitrator_deselect(struct udevice *mux, struct udevice *bus, |
||||
uint channel) |
||||
{ |
||||
struct i2c_arbitrator_priv *priv = dev_get_priv(mux); |
||||
int ret; |
||||
|
||||
debug("%s: %s\n", __func__, mux->name); |
||||
ret = dm_gpio_set_value(&priv->ap_claim, 0); |
||||
udelay(priv->slew_delay_us); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int i2c_arbitrator_select(struct udevice *mux, struct udevice *bus, |
||||
uint channel) |
||||
{ |
||||
struct i2c_arbitrator_priv *priv = dev_get_priv(mux); |
||||
unsigned start; |
||||
int ret; |
||||
|
||||
debug("%s: %s\n", __func__, mux->name); |
||||
/* Start a round of trying to claim the bus */ |
||||
start = get_timer(0); |
||||
do { |
||||
unsigned start_retry; |
||||
int waiting = 0; |
||||
|
||||
/* Indicate that we want to claim the bus */ |
||||
ret = dm_gpio_set_value(&priv->ap_claim, 1); |
||||
if (ret) |
||||
goto err; |
||||
udelay(priv->slew_delay_us); |
||||
|
||||
/* Wait for the EC to release it */ |
||||
start_retry = get_timer(0); |
||||
while (get_timer(start_retry) < priv->wait_retry_ms) { |
||||
ret = dm_gpio_get_value(&priv->ec_claim); |
||||
if (ret < 0) { |
||||
goto err; |
||||
} else if (!ret) { |
||||
/* We got it, so return */ |
||||
return 0; |
||||
} |
||||
|
||||
if (!waiting) |
||||
waiting = 1; |
||||
} |
||||
|
||||
/* It didn't release, so give up, wait, and try again */ |
||||
ret = dm_gpio_set_value(&priv->ap_claim, 0); |
||||
if (ret) |
||||
goto err; |
||||
|
||||
mdelay(priv->wait_retry_ms); |
||||
} while (get_timer(start) < priv->wait_free_ms); |
||||
|
||||
/* Give up, release our claim */ |
||||
printf("I2C: Could not claim bus, timeout %lu\n", get_timer(start)); |
||||
ret = -ETIMEDOUT; |
||||
ret = 0; |
||||
err: |
||||
return ret; |
||||
} |
||||
|
||||
static int i2c_arbitrator_probe(struct udevice *dev) |
||||
{ |
||||
struct i2c_arbitrator_priv *priv = dev_get_priv(dev); |
||||
const void *blob = gd->fdt_blob; |
||||
int node = dev->of_offset; |
||||
int ret; |
||||
|
||||
debug("%s: %s\n", __func__, dev->name); |
||||
priv->slew_delay_us = fdtdec_get_int(blob, node, "slew-delay-us", 0); |
||||
priv->wait_retry_ms = fdtdec_get_int(blob, node, "wait-retry-us", 0) / |
||||
1000; |
||||
priv->wait_free_ms = fdtdec_get_int(blob, node, "wait-free-us", 0) / |
||||
1000; |
||||
ret = gpio_request_by_name(dev, "our-claim-gpio", 0, &priv->ap_claim, |
||||
GPIOD_IS_OUT); |
||||
if (ret) |
||||
goto err; |
||||
ret = gpio_request_by_name(dev, "their-claim-gpios", 0, &priv->ec_claim, |
||||
GPIOD_IS_IN); |
||||
if (ret) |
||||
goto err_ec_gpio; |
||||
|
||||
return 0; |
||||
|
||||
err_ec_gpio: |
||||
dm_gpio_free(dev, &priv->ap_claim); |
||||
err: |
||||
debug("%s: ret=%d\n", __func__, ret); |
||||
return ret; |
||||
} |
||||
|
||||
static int i2c_arbitrator_remove(struct udevice *dev) |
||||
{ |
||||
struct i2c_arbitrator_priv *priv = dev_get_priv(dev); |
||||
|
||||
dm_gpio_free(dev, &priv->ap_claim); |
||||
dm_gpio_free(dev, &priv->ec_claim); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static const struct i2c_mux_ops i2c_arbitrator_ops = { |
||||
.select = i2c_arbitrator_select, |
||||
.deselect = i2c_arbitrator_deselect, |
||||
}; |
||||
|
||||
static const struct udevice_id i2c_arbitrator_ids[] = { |
||||
{ .compatible = "i2c-arb-gpio-challenge" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(i2c_arbitrator) = { |
||||
.name = "i2c_arbitrator", |
||||
.id = UCLASS_I2C_MUX, |
||||
.of_match = i2c_arbitrator_ids, |
||||
.probe = i2c_arbitrator_probe, |
||||
.remove = i2c_arbitrator_remove, |
||||
.ops = &i2c_arbitrator_ops, |
||||
.priv_auto_alloc_size = sizeof(struct i2c_arbitrator_priv), |
||||
}; |
@ -0,0 +1,198 @@ |
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <errno.h> |
||||
#include <i2c.h> |
||||
#include <dm/lists.h> |
||||
#include <dm/root.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
/**
|
||||
* struct i2c_mux: Information the uclass stores about an I2C mux |
||||
* |
||||
* @selected: Currently selected mux, or -1 for none |
||||
* @i2c_bus: I2C bus to use for communcation |
||||
*/ |
||||
struct i2c_mux { |
||||
int selected; |
||||
struct udevice *i2c_bus; |
||||
}; |
||||
|
||||
/**
|
||||
* struct i2c_mux_bus: Information about each bus the mux controls |
||||
* |
||||
* @channel: Channel number used to select this bus |
||||
*/ |
||||
struct i2c_mux_bus { |
||||
uint channel; |
||||
}; |
||||
|
||||
/* Find out the mux channel number */ |
||||
static int i2c_mux_child_post_bind(struct udevice *dev) |
||||
{ |
||||
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev); |
||||
int channel; |
||||
|
||||
channel = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); |
||||
if (channel < 0) |
||||
return -EINVAL; |
||||
plat->channel = channel; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/* Find the I2C buses selected by this mux */ |
||||
static int i2c_mux_post_bind(struct udevice *mux) |
||||
{ |
||||
const void *blob = gd->fdt_blob; |
||||
int ret; |
||||
int offset; |
||||
|
||||
debug("%s: %s\n", __func__, mux->name); |
||||
/*
|
||||
* There is no compatible string in the sub-nodes, so we must manually |
||||
* bind these |
||||
*/ |
||||
for (offset = fdt_first_subnode(blob, mux->of_offset); |
||||
offset > 0; |
||||
offset = fdt_next_subnode(blob, offset)) { |
||||
struct udevice *dev; |
||||
const char *name; |
||||
|
||||
name = fdt_get_name(blob, offset, NULL); |
||||
ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv", name, |
||||
offset, &dev); |
||||
debug(" - bind ret=%d, %s\n", ret, dev ? dev->name : NULL); |
||||
if (ret) |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/* Set up the mux ready for use */ |
||||
static int i2c_mux_post_probe(struct udevice *mux) |
||||
{ |
||||
struct i2c_mux *priv = dev_get_uclass_priv(mux); |
||||
int ret; |
||||
|
||||
debug("%s: %s\n", __func__, mux->name); |
||||
priv->selected = -1; |
||||
|
||||
ret = uclass_get_device_by_phandle(UCLASS_I2C, mux, "i2c-parent", |
||||
&priv->i2c_bus); |
||||
if (ret) |
||||
return ret; |
||||
debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, priv->i2c_bus->name); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int i2c_mux_select(struct udevice *dev) |
||||
{ |
||||
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev); |
||||
struct udevice *mux = dev->parent; |
||||
struct i2c_mux_ops *ops = i2c_mux_get_ops(mux); |
||||
|
||||
if (!ops->select) |
||||
return -ENOSYS; |
||||
|
||||
return ops->select(mux, dev, plat->channel); |
||||
} |
||||
|
||||
int i2c_mux_deselect(struct udevice *dev) |
||||
{ |
||||
struct i2c_mux_bus *plat = dev_get_parent_platdata(dev); |
||||
struct udevice *mux = dev->parent; |
||||
struct i2c_mux_ops *ops = i2c_mux_get_ops(mux); |
||||
|
||||
if (!ops->deselect) |
||||
return -ENOSYS; |
||||
|
||||
return ops->deselect(mux, dev, plat->channel); |
||||
} |
||||
|
||||
static int i2c_mux_bus_set_bus_speed(struct udevice *dev, unsigned int speed) |
||||
{ |
||||
struct udevice *mux = dev->parent; |
||||
struct i2c_mux *priv = dev_get_uclass_priv(mux); |
||||
int ret, ret2; |
||||
|
||||
ret = i2c_mux_select(dev); |
||||
if (ret) |
||||
return ret; |
||||
ret = dm_i2c_set_bus_speed(priv->i2c_bus, speed); |
||||
ret2 = i2c_mux_deselect(dev); |
||||
|
||||
return ret ? ret : ret2; |
||||
} |
||||
|
||||
static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr, |
||||
uint chip_flags) |
||||
{ |
||||
struct udevice *mux = dev->parent; |
||||
struct i2c_mux *priv = dev_get_uclass_priv(mux); |
||||
struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus); |
||||
int ret, ret2; |
||||
|
||||
debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name); |
||||
if (!ops->probe_chip) |
||||
return -ENOSYS; |
||||
ret = i2c_mux_select(dev); |
||||
if (ret) |
||||
return ret; |
||||
ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags); |
||||
ret2 = i2c_mux_deselect(dev); |
||||
|
||||
return ret ? ret : ret2; |
||||
} |
||||
|
||||
static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg, |
||||
int nmsgs) |
||||
{ |
||||
struct udevice *mux = dev->parent; |
||||
struct i2c_mux *priv = dev_get_uclass_priv(mux); |
||||
struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus); |
||||
int ret, ret2; |
||||
|
||||
debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name); |
||||
if (!ops->xfer) |
||||
return -ENOSYS; |
||||
ret = i2c_mux_select(dev); |
||||
if (ret) |
||||
return ret; |
||||
ret = ops->xfer(priv->i2c_bus, msg, nmsgs); |
||||
ret2 = i2c_mux_deselect(dev); |
||||
|
||||
return ret ? ret : ret2; |
||||
} |
||||
|
||||
static const struct dm_i2c_ops i2c_mux_bus_ops = { |
||||
.xfer = i2c_mux_bus_xfer, |
||||
.probe_chip = i2c_mux_bus_probe, |
||||
.set_bus_speed = i2c_mux_bus_set_bus_speed, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(i2c_mux_bus) = { |
||||
.name = "i2c_mux_bus_drv", |
||||
.id = UCLASS_I2C, |
||||
.per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), |
||||
.ops = &i2c_mux_bus_ops, |
||||
}; |
||||
|
||||
UCLASS_DRIVER(i2c_mux) = { |
||||
.id = UCLASS_I2C_MUX, |
||||
.name = "i2c_mux", |
||||
.post_bind = i2c_mux_post_bind, |
||||
.post_probe = i2c_mux_post_probe, |
||||
.per_device_auto_alloc_size = sizeof(struct i2c_mux), |
||||
.per_child_platdata_auto_alloc_size = sizeof(struct i2c_mux_bus), |
||||
.child_post_bind = i2c_mux_child_post_bind, |
||||
}; |
@ -1,310 +0,0 @@ |
||||
/*
|
||||
* Copyright (c) 2012 The Chromium OS Authors. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <errno.h> |
||||
#include <fdtdec.h> |
||||
#include <i2c.h> |
||||
#include <power/pmic.h> |
||||
#include <power/tps65090_pmic.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
#define TPS65090_NAME "TPS65090_PMIC" |
||||
|
||||
/* TPS65090 register addresses */ |
||||
enum { |
||||
REG_IRQ1 = 0, |
||||
REG_CG_CTRL0 = 4, |
||||
REG_CG_STATUS1 = 0xa, |
||||
REG_FET1_CTRL = 0x0f, |
||||
REG_FET2_CTRL, |
||||
REG_FET3_CTRL, |
||||
REG_FET4_CTRL, |
||||
REG_FET5_CTRL, |
||||
REG_FET6_CTRL, |
||||
REG_FET7_CTRL, |
||||
TPS65090_NUM_REGS, |
||||
}; |
||||
|
||||
enum { |
||||
IRQ1_VBATG = 1 << 3, |
||||
CG_CTRL0_ENC_MASK = 0x01, |
||||
|
||||
MAX_FET_NUM = 7, |
||||
MAX_CTRL_READ_TRIES = 5, |
||||
|
||||
/* TPS65090 FET_CTRL register values */ |
||||
FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */ |
||||
FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */ |
||||
FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */ |
||||
FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */ |
||||
FET_CTRL_ENFET = 1 << 0, /* Enable FET */ |
||||
}; |
||||
|
||||
/**
|
||||
* Checks for a valid FET number |
||||
* |
||||
* @param fet_id FET number to check |
||||
* @return 0 if ok, -EINVAL if FET value is out of range |
||||
*/ |
||||
static int tps65090_check_fet(unsigned int fet_id) |
||||
{ |
||||
if (fet_id == 0 || fet_id > MAX_FET_NUM) { |
||||
debug("parameter fet_id is out of range, %u not in 1 ~ %u\n", |
||||
fet_id, MAX_FET_NUM); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* Set the power state for a FET |
||||
* |
||||
* @param pmic pmic structure for the tps65090 |
||||
* @param fet_id Fet number to set (1..MAX_FET_NUM) |
||||
* @param set 1 to power on FET, 0 to power off |
||||
* @return -EIO if we got a comms error, -EAGAIN if the FET failed to |
||||
* change state. If all is ok, returns 0. |
||||
*/ |
||||
static int tps65090_fet_set(struct pmic *pmic, int fet_id, bool set) |
||||
{ |
||||
int retry; |
||||
u32 reg, value; |
||||
|
||||
value = FET_CTRL_ADENFET | FET_CTRL_WAIT; |
||||
if (set) |
||||
value |= FET_CTRL_ENFET; |
||||
|
||||
if (pmic_reg_write(pmic, REG_FET1_CTRL + fet_id - 1, value)) |
||||
return -EIO; |
||||
|
||||
/* Try reading until we get a result */ |
||||
for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { |
||||
if (pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, ®)) |
||||
return -EIO; |
||||
|
||||
/* Check that the fet went into the expected state */ |
||||
if (!!(reg & FET_CTRL_PGFET) == set) |
||||
return 0; |
||||
|
||||
/* If we got a timeout, there is no point in waiting longer */ |
||||
if (reg & FET_CTRL_TOFET) |
||||
break; |
||||
|
||||
mdelay(1); |
||||
} |
||||
|
||||
debug("FET %d: Power good should have set to %d but reg=%#02x\n", |
||||
fet_id, set, reg); |
||||
return -EAGAIN; |
||||
} |
||||
|
||||
int tps65090_fet_enable(unsigned int fet_id) |
||||
{ |
||||
struct pmic *pmic; |
||||
ulong start; |
||||
int loops; |
||||
int ret; |
||||
|
||||
ret = tps65090_check_fet(fet_id); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
pmic = pmic_get(TPS65090_NAME); |
||||
if (!pmic) |
||||
return -EACCES; |
||||
|
||||
start = get_timer(0); |
||||
for (loops = 0;; loops++) { |
||||
ret = tps65090_fet_set(pmic, fet_id, true); |
||||
if (!ret) |
||||
break; |
||||
|
||||
if (get_timer(start) > 100) |
||||
break; |
||||
|
||||
/* Turn it off and try again until we time out */ |
||||
tps65090_fet_set(pmic, fet_id, false); |
||||
} |
||||
|
||||
if (ret) |
||||
debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", |
||||
__func__, fet_id, get_timer(start), loops); |
||||
else if (loops) |
||||
debug("%s: FET%d powered on after %lums, loops=%d\n", |
||||
__func__, fet_id, get_timer(start), loops); |
||||
|
||||
/*
|
||||
* Unfortunately, there are some conditions where the power |
||||
* good bit will be 0, but the fet still comes up. One such |
||||
* case occurs with the lcd backlight. We'll just return 0 here |
||||
* and assume that the fet will eventually come up. |
||||
*/ |
||||
if (ret == -EAGAIN) |
||||
ret = 0; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int tps65090_fet_disable(unsigned int fet_id) |
||||
{ |
||||
struct pmic *pmic; |
||||
int ret; |
||||
|
||||
ret = tps65090_check_fet(fet_id); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
pmic = pmic_get(TPS65090_NAME); |
||||
if (!pmic) |
||||
return -EACCES; |
||||
ret = tps65090_fet_set(pmic, fet_id, false); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int tps65090_fet_is_enabled(unsigned int fet_id) |
||||
{ |
||||
struct pmic *pmic; |
||||
u32 reg; |
||||
int ret; |
||||
|
||||
ret = tps65090_check_fet(fet_id); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
pmic = pmic_get(TPS65090_NAME); |
||||
if (!pmic) |
||||
return -ENODEV; |
||||
ret = pmic_reg_read(pmic, REG_FET1_CTRL + fet_id - 1, ®); |
||||
if (ret) { |
||||
debug("fail to read FET%u_CTRL register over I2C", fet_id); |
||||
return -EIO; |
||||
} |
||||
|
||||
return reg & FET_CTRL_ENFET; |
||||
} |
||||
|
||||
int tps65090_get_charging(void) |
||||
{ |
||||
struct pmic *pmic; |
||||
u32 val; |
||||
int ret; |
||||
|
||||
pmic = pmic_get(TPS65090_NAME); |
||||
if (!pmic) |
||||
return -EACCES; |
||||
|
||||
ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return !!(val & CG_CTRL0_ENC_MASK); |
||||
} |
||||
|
||||
static int tps65090_charger_state(struct pmic *pmic, int state, |
||||
int current) |
||||
{ |
||||
u32 val; |
||||
int ret; |
||||
|
||||
ret = pmic_reg_read(pmic, REG_CG_CTRL0, &val); |
||||
if (!ret) { |
||||
if (state == PMIC_CHARGER_ENABLE) |
||||
val |= CG_CTRL0_ENC_MASK; |
||||
else |
||||
val &= ~CG_CTRL0_ENC_MASK; |
||||
ret = pmic_reg_write(pmic, REG_CG_CTRL0, val); |
||||
} |
||||
if (ret) { |
||||
debug("%s: Failed to read/write register\n", __func__); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int tps65090_get_status(void) |
||||
{ |
||||
struct pmic *pmic; |
||||
u32 val; |
||||
int ret; |
||||
|
||||
pmic = pmic_get(TPS65090_NAME); |
||||
if (!pmic) |
||||
return -EACCES; |
||||
|
||||
ret = pmic_reg_read(pmic, REG_CG_STATUS1, &val); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return val; |
||||
} |
||||
|
||||
static int tps65090_charger_bat_present(struct pmic *pmic) |
||||
{ |
||||
u32 val; |
||||
int ret; |
||||
|
||||
ret = pmic_reg_read(pmic, REG_IRQ1, &val); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return !!(val & IRQ1_VBATG); |
||||
} |
||||
|
||||
static struct power_chrg power_chrg_pmic_ops = { |
||||
.chrg_bat_present = tps65090_charger_bat_present, |
||||
.chrg_state = tps65090_charger_state, |
||||
}; |
||||
|
||||
int tps65090_init(void) |
||||
{ |
||||
struct pmic *p; |
||||
int bus; |
||||
int addr; |
||||
const void *blob = gd->fdt_blob; |
||||
int node, parent; |
||||
|
||||
node = fdtdec_next_compatible(blob, 0, COMPAT_TI_TPS65090); |
||||
if (node < 0) { |
||||
debug("PMIC: No node for PMIC Chip in device tree\n"); |
||||
debug("node = %d\n", node); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
parent = fdt_parent_offset(blob, node); |
||||
if (parent < 0) { |
||||
debug("%s: Cannot find node parent\n", __func__); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
bus = i2c_get_bus_num_fdt(parent); |
||||
if (bus < 0) { |
||||
debug("%s: Cannot find I2C bus\n", __func__); |
||||
return -ENOENT; |
||||
} |
||||
addr = fdtdec_get_int(blob, node, "reg", TPS65090_I2C_ADDR); |
||||
p = pmic_alloc(); |
||||
if (!p) { |
||||
printf("%s: POWER allocation error!\n", __func__); |
||||
return -ENOMEM; |
||||
} |
||||
|
||||
p->name = TPS65090_NAME; |
||||
p->bus = bus; |
||||
p->interface = PMIC_I2C; |
||||
p->number_of_regs = TPS65090_NUM_REGS; |
||||
p->hw.i2c.addr = addr; |
||||
p->hw.i2c.tx_num = 1; |
||||
p->chrg = &power_chrg_pmic_ops; |
||||
|
||||
puts("TPS65090 PMIC init\n"); |
||||
|
||||
return 0; |
||||
} |
@ -1,218 +0,0 @@ |
||||
/*
|
||||
* Copyright (c) 2013 The Chromium OS Authors. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <cros_ec.h> |
||||
#include <errno.h> |
||||
#include <power/tps65090_pmic.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
#define TPS65090_ADDR 0x48 |
||||
|
||||
static struct tps65090 { |
||||
struct cros_ec_dev *dev; /* The CROS_EC device */ |
||||
} config; |
||||
|
||||
/* TPS65090 register addresses */ |
||||
enum { |
||||
REG_IRQ1 = 0, |
||||
REG_CG_CTRL0 = 4, |
||||
REG_CG_STATUS1 = 0xa, |
||||
REG_FET1_CTRL = 0x0f, |
||||
REG_FET2_CTRL, |
||||
REG_FET3_CTRL, |
||||
REG_FET4_CTRL, |
||||
REG_FET5_CTRL, |
||||
REG_FET6_CTRL, |
||||
REG_FET7_CTRL, |
||||
TPS65090_NUM_REGS, |
||||
}; |
||||
|
||||
enum { |
||||
IRQ1_VBATG = 1 << 3, |
||||
CG_CTRL0_ENC_MASK = 0x01, |
||||
|
||||
MAX_FET_NUM = 7, |
||||
MAX_CTRL_READ_TRIES = 5, |
||||
|
||||
/* TPS65090 FET_CTRL register values */ |
||||
FET_CTRL_TOFET = 1 << 7, /* Timeout, startup, overload */ |
||||
FET_CTRL_PGFET = 1 << 4, /* Power good for FET status */ |
||||
FET_CTRL_WAIT = 3 << 2, /* Overcurrent timeout max */ |
||||
FET_CTRL_ADENFET = 1 << 1, /* Enable output auto discharge */ |
||||
FET_CTRL_ENFET = 1 << 0, /* Enable FET */ |
||||
}; |
||||
|
||||
/**
|
||||
* tps65090_read - read a byte from tps6090 |
||||
* |
||||
* @param reg The register address to read from. |
||||
* @param val We'll return value value read here. |
||||
* @return 0 if ok; error if EC returns failure. |
||||
*/ |
||||
static int tps65090_read(u32 reg, u8 *val) |
||||
{ |
||||
return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1, |
||||
val, 1, true); |
||||
} |
||||
|
||||
/**
|
||||
* tps65090_write - write a byte to tps6090 |
||||
* |
||||
* @param reg The register address to write to. |
||||
* @param val The value to write. |
||||
* @return 0 if ok; error if EC returns failure. |
||||
*/ |
||||
static int tps65090_write(u32 reg, u8 val) |
||||
{ |
||||
return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1, |
||||
&val, 1, false); |
||||
} |
||||
|
||||
/**
|
||||
* Checks for a valid FET number |
||||
* |
||||
* @param fet_id FET number to check |
||||
* @return 0 if ok, -EINVAL if FET value is out of range |
||||
*/ |
||||
static int tps65090_check_fet(unsigned int fet_id) |
||||
{ |
||||
if (fet_id == 0 || fet_id > MAX_FET_NUM) { |
||||
debug("parameter fet_id is out of range, %u not in 1 ~ %u\n", |
||||
fet_id, MAX_FET_NUM); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/**
|
||||
* Set the power state for a FET |
||||
* |
||||
* @param fet_id Fet number to set (1..MAX_FET_NUM) |
||||
* @param set 1 to power on FET, 0 to power off |
||||
* @return -EIO if we got a comms error, -EAGAIN if the FET failed to |
||||
* change state. If all is ok, returns 0. |
||||
*/ |
||||
static int tps65090_fet_set(int fet_id, bool set) |
||||
{ |
||||
int retry; |
||||
u8 reg, value; |
||||
|
||||
value = FET_CTRL_ADENFET | FET_CTRL_WAIT; |
||||
if (set) |
||||
value |= FET_CTRL_ENFET; |
||||
|
||||
if (tps65090_write(REG_FET1_CTRL + fet_id - 1, value)) |
||||
return -EIO; |
||||
|
||||
/* Try reading until we get a result */ |
||||
for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { |
||||
if (tps65090_read(REG_FET1_CTRL + fet_id - 1, ®)) |
||||
return -EIO; |
||||
|
||||
/* Check that the fet went into the expected state */ |
||||
if (!!(reg & FET_CTRL_PGFET) == set) |
||||
return 0; |
||||
|
||||
/* If we got a timeout, there is no point in waiting longer */ |
||||
if (reg & FET_CTRL_TOFET) |
||||
break; |
||||
|
||||
mdelay(1); |
||||
} |
||||
|
||||
debug("FET %d: Power good should have set to %d but reg=%#02x\n", |
||||
fet_id, set, reg); |
||||
return -EAGAIN; |
||||
} |
||||
|
||||
int tps65090_fet_enable(unsigned int fet_id) |
||||
{ |
||||
ulong start; |
||||
int loops; |
||||
int ret; |
||||
|
||||
ret = tps65090_check_fet(fet_id); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
start = get_timer(0); |
||||
for (loops = 0;; loops++) { |
||||
ret = tps65090_fet_set(fet_id, true); |
||||
if (!ret) |
||||
break; |
||||
|
||||
if (get_timer(start) > 100) |
||||
break; |
||||
|
||||
/* Turn it off and try again until we time out */ |
||||
tps65090_fet_set(fet_id, false); |
||||
} |
||||
|
||||
if (ret) { |
||||
debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", |
||||
__func__, fet_id, get_timer(start), loops); |
||||
} else if (loops) { |
||||
debug("%s: FET%d powered on after %lums, loops=%d\n", |
||||
__func__, fet_id, get_timer(start), loops); |
||||
} |
||||
/*
|
||||
* Unfortunately, there are some conditions where the power |
||||
* good bit will be 0, but the fet still comes up. One such |
||||
* case occurs with the lcd backlight. We'll just return 0 here |
||||
* and assume that the fet will eventually come up. |
||||
*/ |
||||
if (ret == -EAGAIN) |
||||
ret = 0; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int tps65090_fet_disable(unsigned int fet_id) |
||||
{ |
||||
int ret; |
||||
|
||||
ret = tps65090_check_fet(fet_id); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
ret = tps65090_fet_set(fet_id, false); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
int tps65090_fet_is_enabled(unsigned int fet_id) |
||||
{ |
||||
u8 reg = 0; |
||||
int ret; |
||||
|
||||
ret = tps65090_check_fet(fet_id); |
||||
if (ret) |
||||
return ret; |
||||
ret = tps65090_read(REG_FET1_CTRL + fet_id - 1, ®); |
||||
if (ret) { |
||||
debug("fail to read FET%u_CTRL register over I2C", fet_id); |
||||
return -EIO; |
||||
} |
||||
|
||||
return reg & FET_CTRL_ENFET; |
||||
} |
||||
|
||||
int tps65090_init(void) |
||||
{ |
||||
puts("TPS65090 PMIC EC init\n"); |
||||
|
||||
config.dev = board_get_cros_ec_dev(); |
||||
if (!config.dev) { |
||||
debug("%s: no cros_ec device: cannot init tps65090\n", |
||||
__func__); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,95 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fdtdec.h> |
||||
#include <errno.h> |
||||
#include <dm.h> |
||||
#include <i2c.h> |
||||
#include <power/pmic.h> |
||||
#include <power/regulator.h> |
||||
#include <power/s5m8767.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
static const struct pmic_child_info pmic_children_info[] = { |
||||
{ .prefix = "LDO", .driver = S5M8767_LDO_DRIVER }, |
||||
{ .prefix = "BUCK", .driver = S5M8767_BUCK_DRIVER }, |
||||
{ }, |
||||
}; |
||||
|
||||
static int s5m8767_reg_count(struct udevice *dev) |
||||
{ |
||||
return S5M8767_NUM_OF_REGS; |
||||
} |
||||
|
||||
static int s5m8767_write(struct udevice *dev, uint reg, const uint8_t *buff, |
||||
int len) |
||||
{ |
||||
if (dm_i2c_write(dev, reg, buff, len)) { |
||||
error("write error to device: %p register: %#x!", dev, reg); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int s5m8767_read(struct udevice *dev, uint reg, uint8_t *buff, int len) |
||||
{ |
||||
if (dm_i2c_read(dev, reg, buff, len)) { |
||||
error("read error from device: %p register: %#x!", dev, reg); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int s5m8767_enable_32khz_cp(struct udevice *dev) |
||||
{ |
||||
return pmic_clrsetbits(dev, S5M8767_EN32KHZ_CP, 0, 1 << 1); |
||||
} |
||||
|
||||
static int s5m8767_bind(struct udevice *dev) |
||||
{ |
||||
int node; |
||||
const void *blob = gd->fdt_blob; |
||||
int children; |
||||
|
||||
node = fdt_subnode_offset(blob, dev->of_offset, "regulators"); |
||||
if (node <= 0) { |
||||
debug("%s: %s regulators subnode not found!", __func__, |
||||
dev->name); |
||||
return -ENXIO; |
||||
} |
||||
|
||||
debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); |
||||
|
||||
children = pmic_bind_children(dev, node, pmic_children_info); |
||||
if (!children) |
||||
debug("%s: %s - no child found\n", __func__, dev->name); |
||||
|
||||
/* Always return success for this device */ |
||||
return 0; |
||||
} |
||||
|
||||
static struct dm_pmic_ops s5m8767_ops = { |
||||
.reg_count = s5m8767_reg_count, |
||||
.read = s5m8767_read, |
||||
.write = s5m8767_write, |
||||
}; |
||||
|
||||
static const struct udevice_id s5m8767_ids[] = { |
||||
{ .compatible = "samsung,s5m8767-pmic" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(pmic_s5m8767) = { |
||||
.name = "s5m8767_pmic", |
||||
.id = UCLASS_PMIC, |
||||
.of_match = s5m8767_ids, |
||||
.bind = s5m8767_bind, |
||||
.ops = &s5m8767_ops, |
||||
}; |
@ -0,0 +1,94 @@ |
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <errno.h> |
||||
#include <fdtdec.h> |
||||
#include <i2c.h> |
||||
#include <power/pmic.h> |
||||
#include <power/tps65090.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
static const struct pmic_child_info pmic_children_info[] = { |
||||
{ .prefix = "fet", .driver = TPS65090_FET_DRIVER }, |
||||
{ }, |
||||
}; |
||||
|
||||
static int tps65090_reg_count(struct udevice *dev) |
||||
{ |
||||
return TPS65090_NUM_REGS; |
||||
} |
||||
|
||||
static int tps65090_write(struct udevice *dev, uint reg, const uint8_t *buff, |
||||
int len) |
||||
{ |
||||
if (dm_i2c_write(dev, reg, buff, len)) { |
||||
error("write error to device: %p register: %#x!", dev, reg); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int tps65090_read(struct udevice *dev, uint reg, uint8_t *buff, int len) |
||||
{ |
||||
int ret; |
||||
|
||||
ret = dm_i2c_read(dev, reg, buff, len); |
||||
if (ret) { |
||||
error("read error %d from device: %p register: %#x!", ret, dev, |
||||
reg); |
||||
return -EIO; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int tps65090_bind(struct udevice *dev) |
||||
{ |
||||
int regulators_node; |
||||
const void *blob = gd->fdt_blob; |
||||
int children; |
||||
|
||||
regulators_node = fdt_subnode_offset(blob, dev->of_offset, |
||||
"regulators"); |
||||
if (regulators_node <= 0) { |
||||
debug("%s: %s regulators subnode not found!", __func__, |
||||
dev->name); |
||||
return -ENXIO; |
||||
} |
||||
|
||||
debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); |
||||
|
||||
children = pmic_bind_children(dev, regulators_node, pmic_children_info); |
||||
if (!children) |
||||
debug("%s: %s - no child found\n", __func__, dev->name); |
||||
|
||||
/* Always return success for this device */ |
||||
return 0; |
||||
} |
||||
|
||||
static struct dm_pmic_ops tps65090_ops = { |
||||
.reg_count = tps65090_reg_count, |
||||
.read = tps65090_read, |
||||
.write = tps65090_write, |
||||
}; |
||||
|
||||
static const struct udevice_id tps65090_ids[] = { |
||||
{ .compatible = "ti,tps65090" }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(pmic_tps65090) = { |
||||
.name = "tps65090 pmic", |
||||
.id = UCLASS_PMIC, |
||||
.of_match = tps65090_ids, |
||||
.bind = tps65090_bind, |
||||
.ops = &tps65090_ops, |
||||
}; |
@ -0,0 +1,269 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <fdtdec.h> |
||||
#include <errno.h> |
||||
#include <dm.h> |
||||
#include <i2c.h> |
||||
#include <power/pmic.h> |
||||
#include <power/regulator.h> |
||||
#include <power/s5m8767.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
static const struct sec_voltage_desc buck_v1 = { |
||||
.max = 2225000, |
||||
.min = 650000, |
||||
.step = 6250, |
||||
}; |
||||
|
||||
static const struct sec_voltage_desc buck_v2 = { |
||||
.max = 1600000, |
||||
.min = 600000, |
||||
.step = 6250, |
||||
}; |
||||
|
||||
static const struct sec_voltage_desc buck_v3 = { |
||||
.max = 3000000, |
||||
.min = 750000, |
||||
.step = 12500, |
||||
}; |
||||
|
||||
static const struct sec_voltage_desc ldo_v1 = { |
||||
.max = 3950000, |
||||
.min = 800000, |
||||
.step = 50000, |
||||
}; |
||||
|
||||
static const struct sec_voltage_desc ldo_v2 = { |
||||
.max = 2375000, |
||||
.min = 800000, |
||||
.step = 25000, |
||||
}; |
||||
|
||||
static const struct s5m8767_para buck_param[] = { |
||||
/*
|
||||
* | voltage ----| | enable -| voltage |
||||
* regnum addr bpos mask addr on desc |
||||
*/ |
||||
{S5M8767_BUCK1, 0x33, 0x0, 0xff, 0x32, 0x3, &buck_v1}, |
||||
{S5M8767_BUCK2, 0x35, 0x0, 0xff, 0x34, 0x1, &buck_v2}, |
||||
{S5M8767_BUCK3, 0x3e, 0x0, 0xff, 0x3d, 0x1, &buck_v2}, |
||||
{S5M8767_BUCK4, 0x47, 0x0, 0xff, 0x46, 0x1, &buck_v2}, |
||||
{S5M8767_BUCK5, 0x50, 0x0, 0xff, 0x4f, 0x3, &buck_v1}, |
||||
{S5M8767_BUCK6, 0x55, 0x0, 0xff, 0x54, 0x3, &buck_v1}, |
||||
{S5M8767_BUCK7, 0x57, 0x0, 0xff, 0x56, 0x3, &buck_v3}, |
||||
{S5M8767_BUCK8, 0x59, 0x0, 0xff, 0x58, 0x3, &buck_v3}, |
||||
{S5M8767_BUCK9, 0x5b, 0x0, 0xff, 0x5a, 0x3, &buck_v3}, |
||||
}; |
||||
|
||||
static const struct s5m8767_para ldo_param[] = { |
||||
{S5M8767_LDO1, 0x5c, 0x0, 0x3f, 0x5c, 0x3, &ldo_v2}, |
||||
{S5M8767_LDO2, 0x5d, 0x0, 0x3f, 0x5d, 0x1, &ldo_v2}, |
||||
{S5M8767_LDO3, 0x61, 0x0, 0x3f, 0x61, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO4, 0x62, 0x0, 0x3f, 0x62, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO5, 0x63, 0x0, 0x3f, 0x63, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO6, 0x64, 0x0, 0x3f, 0x64, 0x1, &ldo_v2}, |
||||
{S5M8767_LDO7, 0x65, 0x0, 0x3f, 0x65, 0x1, &ldo_v2}, |
||||
{S5M8767_LDO8, 0x66, 0x0, 0x3f, 0x66, 0x1, &ldo_v2}, |
||||
{S5M8767_LDO9, 0x67, 0x0, 0x3f, 0x67, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO10, 0x68, 0x0, 0x3f, 0x68, 0x1, &ldo_v1}, |
||||
{S5M8767_LDO11, 0x69, 0x0, 0x3f, 0x69, 0x1, &ldo_v1}, |
||||
{S5M8767_LDO12, 0x6a, 0x0, 0x3f, 0x6a, 0x1, &ldo_v1}, |
||||
{S5M8767_LDO13, 0x6b, 0x0, 0x3f, 0x6b, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO14, 0x6c, 0x0, 0x3f, 0x6c, 0x1, &ldo_v1}, |
||||
{S5M8767_LDO15, 0x6d, 0x0, 0x3f, 0x6d, 0x1, &ldo_v2}, |
||||
{S5M8767_LDO16, 0x6e, 0x0, 0x3f, 0x6e, 0x1, &ldo_v1}, |
||||
{S5M8767_LDO17, 0x6f, 0x0, 0x3f, 0x6f, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO18, 0x70, 0x0, 0x3f, 0x70, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO19, 0x71, 0x0, 0x3f, 0x71, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO20, 0x72, 0x0, 0x3f, 0x72, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO21, 0x73, 0x0, 0x3f, 0x73, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO22, 0x74, 0x0, 0x3f, 0x74, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO23, 0x75, 0x0, 0x3f, 0x75, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO24, 0x76, 0x0, 0x3f, 0x76, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO25, 0x77, 0x0, 0x3f, 0x77, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO26, 0x78, 0x0, 0x3f, 0x78, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO27, 0x79, 0x0, 0x3f, 0x79, 0x3, &ldo_v1}, |
||||
{S5M8767_LDO28, 0x7a, 0x0, 0x3f, 0x7a, 0x3, &ldo_v1}, |
||||
}; |
||||
|
||||
enum { |
||||
ENABLE_SHIFT = 6, |
||||
ENABLE_MASK = 3, |
||||
}; |
||||
|
||||
static int reg_get_value(struct udevice *dev, const struct s5m8767_para *param) |
||||
{ |
||||
const struct sec_voltage_desc *desc; |
||||
int ret, uv, val; |
||||
|
||||
ret = pmic_reg_read(dev->parent, param->vol_addr); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
desc = param->vol; |
||||
val = (ret >> param->vol_bitpos) & param->vol_bitmask; |
||||
uv = desc->min + val * desc->step; |
||||
|
||||
return uv; |
||||
} |
||||
|
||||
static int reg_set_value(struct udevice *dev, const struct s5m8767_para *param, |
||||
int uv) |
||||
{ |
||||
const struct sec_voltage_desc *desc; |
||||
int ret, val; |
||||
|
||||
desc = param->vol; |
||||
if (uv < desc->min || uv > desc->max) |
||||
return -EINVAL; |
||||
val = (uv - desc->min) / desc->step; |
||||
val = (val & param->vol_bitmask) << param->vol_bitpos; |
||||
ret = pmic_clrsetbits(dev->parent, param->vol_addr, |
||||
param->vol_bitmask << param->vol_bitpos, |
||||
val); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int s5m8767_ldo_probe(struct udevice *dev) |
||||
{ |
||||
struct dm_regulator_uclass_platdata *uc_pdata; |
||||
|
||||
uc_pdata = dev_get_uclass_platdata(dev); |
||||
|
||||
uc_pdata->type = REGULATOR_TYPE_LDO; |
||||
uc_pdata->mode_count = 0; |
||||
|
||||
return 0; |
||||
} |
||||
static int ldo_get_value(struct udevice *dev) |
||||
{ |
||||
int ldo = dev->driver_data; |
||||
|
||||
return reg_get_value(dev, &ldo_param[ldo]); |
||||
} |
||||
|
||||
static int ldo_set_value(struct udevice *dev, int uv) |
||||
{ |
||||
int ldo = dev->driver_data; |
||||
|
||||
return reg_set_value(dev, &ldo_param[ldo], uv); |
||||
} |
||||
|
||||
static int reg_get_enable(struct udevice *dev, const struct s5m8767_para *param) |
||||
{ |
||||
bool enable; |
||||
int ret; |
||||
|
||||
ret = pmic_reg_read(dev->parent, param->reg_enaddr); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
enable = (ret >> ENABLE_SHIFT) & ENABLE_MASK; |
||||
|
||||
return enable; |
||||
} |
||||
|
||||
static int reg_set_enable(struct udevice *dev, const struct s5m8767_para *param, |
||||
bool enable) |
||||
{ |
||||
int ret; |
||||
|
||||
ret = pmic_reg_read(dev->parent, param->reg_enaddr); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
ret = pmic_clrsetbits(dev->parent, param->reg_enaddr, |
||||
ENABLE_MASK << ENABLE_SHIFT, |
||||
enable ? param->reg_enbiton << ENABLE_SHIFT : 0); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static bool ldo_get_enable(struct udevice *dev) |
||||
{ |
||||
int ldo = dev->driver_data; |
||||
|
||||
return reg_get_enable(dev, &ldo_param[ldo]); |
||||
} |
||||
|
||||
static int ldo_set_enable(struct udevice *dev, bool enable) |
||||
{ |
||||
int ldo = dev->driver_data; |
||||
|
||||
return reg_set_enable(dev, &ldo_param[ldo], enable); |
||||
} |
||||
|
||||
static int s5m8767_buck_probe(struct udevice *dev) |
||||
{ |
||||
struct dm_regulator_uclass_platdata *uc_pdata; |
||||
|
||||
uc_pdata = dev_get_uclass_platdata(dev); |
||||
|
||||
uc_pdata->type = REGULATOR_TYPE_BUCK; |
||||
uc_pdata->mode_count = 0; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int buck_get_value(struct udevice *dev) |
||||
{ |
||||
int buck = dev->driver_data; |
||||
|
||||
return reg_get_value(dev, &buck_param[buck]); |
||||
} |
||||
|
||||
static int buck_set_value(struct udevice *dev, int uv) |
||||
{ |
||||
int buck = dev->driver_data; |
||||
|
||||
return reg_set_value(dev, &buck_param[buck], uv); |
||||
} |
||||
|
||||
static bool buck_get_enable(struct udevice *dev) |
||||
{ |
||||
int buck = dev->driver_data; |
||||
|
||||
return reg_get_enable(dev, &buck_param[buck]); |
||||
} |
||||
|
||||
static int buck_set_enable(struct udevice *dev, bool enable) |
||||
{ |
||||
int buck = dev->driver_data; |
||||
|
||||
return reg_set_enable(dev, &buck_param[buck], enable); |
||||
} |
||||
|
||||
static const struct dm_regulator_ops s5m8767_ldo_ops = { |
||||
.get_value = ldo_get_value, |
||||
.set_value = ldo_set_value, |
||||
.get_enable = ldo_get_enable, |
||||
.set_enable = ldo_set_enable, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(s5m8767_ldo) = { |
||||
.name = S5M8767_LDO_DRIVER, |
||||
.id = UCLASS_REGULATOR, |
||||
.ops = &s5m8767_ldo_ops, |
||||
.probe = s5m8767_ldo_probe, |
||||
}; |
||||
|
||||
static const struct dm_regulator_ops s5m8767_buck_ops = { |
||||
.get_value = buck_get_value, |
||||
.set_value = buck_set_value, |
||||
.get_enable = buck_get_enable, |
||||
.set_enable = buck_set_enable, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(s5m8767_buck) = { |
||||
.name = S5M8767_BUCK_DRIVER, |
||||
.id = UCLASS_REGULATOR, |
||||
.ops = &s5m8767_buck_ops, |
||||
.probe = s5m8767_buck_probe, |
||||
}; |
@ -0,0 +1,138 @@ |
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <errno.h> |
||||
#include <power/pmic.h> |
||||
#include <power/regulator.h> |
||||
#include <power/tps65090.h> |
||||
|
||||
static int tps65090_fet_probe(struct udevice *dev) |
||||
{ |
||||
struct dm_regulator_uclass_platdata *uc_pdata; |
||||
|
||||
uc_pdata = dev_get_uclass_platdata(dev); |
||||
|
||||
uc_pdata->type = REGULATOR_TYPE_OTHER; |
||||
uc_pdata->mode_count = 0; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static bool tps65090_fet_get_enable(struct udevice *dev) |
||||
{ |
||||
struct udevice *pmic = dev_get_parent(dev); |
||||
int ret, fet_id; |
||||
|
||||
fet_id = dev->driver_data; |
||||
debug("%s: fet_id=%d\n", __func__, fet_id); |
||||
|
||||
ret = pmic_reg_read(pmic, REG_FET_BASE + fet_id); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
return ret & FET_CTRL_ENFET; |
||||
} |
||||
|
||||
/**
|
||||
* Set the power state for a FET |
||||
* |
||||
* @param pmic pmic structure for the tps65090 |
||||
* @param fet_id FET number to set (1..MAX_FET_NUM) |
||||
* @param set 1 to power on FET, 0 to power off |
||||
* @return -EIO if we got a comms error, -EAGAIN if the FET failed to |
||||
* change state. If all is ok, returns 0. |
||||
*/ |
||||
static int tps65090_fet_set(struct udevice *pmic, int fet_id, bool set) |
||||
{ |
||||
int retry; |
||||
u32 value; |
||||
int ret; |
||||
|
||||
value = FET_CTRL_ADENFET | FET_CTRL_WAIT; |
||||
if (set) |
||||
value |= FET_CTRL_ENFET; |
||||
|
||||
if (pmic_reg_write(pmic, REG_FET_BASE + fet_id, value)) |
||||
return -EIO; |
||||
|
||||
/* Try reading until we get a result */ |
||||
for (retry = 0; retry < MAX_CTRL_READ_TRIES; retry++) { |
||||
ret = pmic_reg_read(pmic, REG_FET_BASE + fet_id); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
/* Check that the FET went into the expected state */ |
||||
debug("%s: flags=%x\n", __func__, ret); |
||||
if (!!(ret & FET_CTRL_PGFET) == set) |
||||
return 0; |
||||
|
||||
/* If we got a timeout, there is no point in waiting longer */ |
||||
if (ret & FET_CTRL_TOFET) |
||||
break; |
||||
|
||||
mdelay(1); |
||||
} |
||||
|
||||
debug("FET %d: Power good should have set to %d but reg=%#02x\n", |
||||
fet_id, set, ret); |
||||
return -EAGAIN; |
||||
} |
||||
|
||||
static int tps65090_fet_set_enable(struct udevice *dev, bool enable) |
||||
{ |
||||
struct udevice *pmic = dev_get_parent(dev); |
||||
int ret, fet_id; |
||||
ulong start; |
||||
int loops; |
||||
|
||||
fet_id = dev->driver_data; |
||||
debug("%s: fet_id=%d, enable=%d\n", __func__, fet_id, enable); |
||||
|
||||
start = get_timer(0); |
||||
for (loops = 0;; loops++) { |
||||
ret = tps65090_fet_set(pmic, fet_id, enable); |
||||
if (!ret) |
||||
break; |
||||
|
||||
if (get_timer(start) > 100) |
||||
break; |
||||
|
||||
/* Turn it off and try again until we time out */ |
||||
tps65090_fet_set(pmic, fet_id, false); |
||||
} |
||||
|
||||
if (ret) |
||||
debug("%s: FET%d failed to power on: time=%lums, loops=%d\n", |
||||
__func__, fet_id, get_timer(start), loops); |
||||
else if (loops) |
||||
debug("%s: FET%d powered on after %lums, loops=%d\n", |
||||
__func__, fet_id, get_timer(start), loops); |
||||
|
||||
/*
|
||||
* Unfortunately there are some conditions where the power-good bit |
||||
* will be 0, but the FET still comes up. One such case occurs with |
||||
* the LCD backlight on snow. We'll just return 0 here and assume |
||||
* that the FET will eventually come up. |
||||
*/ |
||||
if (ret == -EAGAIN) |
||||
ret = 0; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static const struct dm_regulator_ops tps65090_fet_ops = { |
||||
.get_enable = tps65090_fet_get_enable, |
||||
.set_enable = tps65090_fet_set_enable, |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(tps65090_fet) = { |
||||
.name = TPS65090_FET_DRIVER, |
||||
.id = UCLASS_REGULATOR, |
||||
.ops = &tps65090_fet_ops, |
||||
.probe = tps65090_fet_probe, |
||||
}; |
@ -0,0 +1,27 @@ |
||||
config VIDEO_BRIDGE |
||||
bool "Support video bridges" |
||||
depends on DM |
||||
help |
||||
Some platforms use video bridges to convert from one output to |
||||
another. For example, where the SoC only supports eDP and the LCD |
||||
requires LVDS, an eDP->LVDS bridge chip can be used to provide the |
||||
necessary conversion. This option enables support for these devices. |
||||
|
||||
config VIDEO_BRIDGE_PARADE_PS862X |
||||
bool "Support Parade PS862X DP->LVDS bridge" |
||||
depends on VIDEO_BRIDGE |
||||
help |
||||
The Parade PS8622 and PS8625 are DisplayPort-to-LVDS (Low voltage |
||||
differential signalling) converters. They enable an LVDS LCD panel |
||||
to be connected to an eDP output device such as an SoC that lacks |
||||
LVDS capability, or where LVDS requires too many signals to route |
||||
on the PCB. Setup parameters are provided in the device tree. |
||||
|
||||
config VIDEO_BRIDGE_NXP_PTN3460 |
||||
bool "Support NXP PTN3460 DP->LVDS bridge" |
||||
depends on VIDEO_BRIDGE |
||||
help |
||||
The NXP PTN3460 is a DisplayPort-to-LVDS (Low voltage differential |
||||
signalling) converter. It enables an LVDS LCD panel to be connected |
||||
to an eDP output device such as an SoC that lacks LVDS capability, |
||||
or where LVDS requires too many signals to route on the PCB. |
@ -0,0 +1,9 @@ |
||||
#
|
||||
# Copyright (C) 2015 Google, Inc
|
||||
# Written by Simon Glass <sjg@chromium.org>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
obj-$(CONFIG_VIDEO_BRIDGE) += video-bridge-uclass.o
|
||||
obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o
|
||||
obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o
|
@ -0,0 +1,134 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <errno.h> |
||||
#include <i2c.h> |
||||
#include <video_bridge.h> |
||||
#include <power/regulator.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
/*
|
||||
* Initialisation of the chip is a process of writing certain values into |
||||
* certain registers over i2c bus. The chip in fact responds to a range of |
||||
* addresses on the i2c bus, so for each written value three parameters are |
||||
* required: i2c address, register address and the actual value. |
||||
* |
||||
* The base address is derived from the device tree, but oddly the chip |
||||
* responds on several addresses with different register sets for each. |
||||
*/ |
||||
|
||||
/**
|
||||
* ps8622_write() Write a PS8622 eDP bridge i2c register |
||||
* |
||||
* @param dev I2C device |
||||
* @param addr_off offset from the i2c base address for ps8622 |
||||
* @param reg_addr register address to write |
||||
* @param value value to be written |
||||
* @return 0 on success, non-0 on failure |
||||
*/ |
||||
static int ps8622_write(struct udevice *dev, unsigned addr_off, |
||||
unsigned char reg_addr, unsigned char value) |
||||
{ |
||||
struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); |
||||
uint8_t buf[2]; |
||||
struct i2c_msg msg; |
||||
int ret; |
||||
|
||||
msg.addr = chip->chip_addr + addr_off; |
||||
msg.flags = 0; |
||||
buf[0] = reg_addr; |
||||
buf[1] = value; |
||||
msg.buf = buf; |
||||
msg.len = 2; |
||||
ret = dm_i2c_xfer(dev, &msg, 1); |
||||
if (ret) { |
||||
debug("%s: write failed, reg=%#x, value=%#x, ret=%d\n", |
||||
__func__, reg_addr, value, ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int ps8622_set_backlight(struct udevice *dev, int percent) |
||||
{ |
||||
int level = percent * 255 / 100; |
||||
|
||||
debug("%s: level=%d\n", __func__, level); |
||||
return ps8622_write(dev, 0x01, 0xa7, level); |
||||
} |
||||
|
||||
static int ps8622_attach(struct udevice *dev) |
||||
{ |
||||
const uint8_t *params; |
||||
struct udevice *reg; |
||||
int ret, i, len; |
||||
|
||||
debug("%s: %s\n", __func__, dev->name); |
||||
/* set the LDO providing the 1.2V rail to the Parade bridge */ |
||||
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, |
||||
"power-supply", ®); |
||||
if (!ret) { |
||||
ret = regulator_autoset(reg); |
||||
} else if (ret != -ENOENT) { |
||||
debug("%s: Failed to enable power: ret=%d\n", __func__, ret); |
||||
return ret; |
||||
} |
||||
|
||||
ret = video_bridge_set_active(dev, true); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
params = fdt_getprop(gd->fdt_blob, dev->of_offset, "parade,regs", &len); |
||||
if (!params || len % 3) { |
||||
debug("%s: missing/invalid params=%p, len=%x\n", __func__, |
||||
params, len); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* need to wait 20ms after power on before doing I2C writes */ |
||||
mdelay(20); |
||||
for (i = 0; i < len; i += 3) { |
||||
ret = ps8622_write(dev, params[i + 0], params[i + 1], |
||||
params[i + 2]); |
||||
if (ret) |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int ps8622_probe(struct udevice *dev) |
||||
{ |
||||
debug("%s\n", __func__); |
||||
if (device_get_uclass_id(dev->parent) != UCLASS_I2C) |
||||
return -EPROTONOSUPPORT; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
struct video_bridge_ops ps8622_ops = { |
||||
.attach = ps8622_attach, |
||||
.set_backlight = ps8622_set_backlight, |
||||
}; |
||||
|
||||
static const struct udevice_id ps8622_ids[] = { |
||||
{ .compatible = "parade,ps8622", }, |
||||
{ .compatible = "parade,ps8625", }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(parade_ps8622) = { |
||||
.name = "parade_ps8622", |
||||
.id = UCLASS_VIDEO_BRIDGE, |
||||
.of_match = ps8622_ids, |
||||
.probe = ps8622_probe, |
||||
.ops = &ps8622_ops, |
||||
}; |
@ -0,0 +1,38 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <video_bridge.h> |
||||
|
||||
static int ptn3460_attach(struct udevice *dev) |
||||
{ |
||||
int ret; |
||||
|
||||
debug("%s: %s\n", __func__, dev->name); |
||||
ret = video_bridge_set_active(dev, true); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
struct video_bridge_ops ptn3460_ops = { |
||||
.attach = ptn3460_attach, |
||||
}; |
||||
|
||||
static const struct udevice_id ptn3460_ids[] = { |
||||
{ .compatible = "nxp,ptn3460", }, |
||||
{ } |
||||
}; |
||||
|
||||
U_BOOT_DRIVER(parade_ptn3460) = { |
||||
.name = "nmp_ptn3460", |
||||
.id = UCLASS_VIDEO_BRIDGE, |
||||
.of_match = ptn3460_ids, |
||||
.ops = &ptn3460_ops, |
||||
}; |
@ -0,0 +1,119 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Google, Inc |
||||
* Written by Simon Glass <sjg@chromium.org> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <dm.h> |
||||
#include <errno.h> |
||||
#include <video_bridge.h> |
||||
|
||||
int video_bridge_set_backlight(struct udevice *dev, int percent) |
||||
{ |
||||
struct video_bridge_ops *ops = video_bridge_get_ops(dev); |
||||
|
||||
if (!ops->set_backlight) |
||||
return -ENOSYS; |
||||
|
||||
return ops->set_backlight(dev, percent); |
||||
} |
||||
|
||||
int video_bridge_attach(struct udevice *dev) |
||||
{ |
||||
struct video_bridge_ops *ops = video_bridge_get_ops(dev); |
||||
|
||||
if (!ops->attach) |
||||
return -ENOSYS; |
||||
|
||||
return ops->attach(dev); |
||||
} |
||||
|
||||
int video_bridge_check_attached(struct udevice *dev) |
||||
{ |
||||
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); |
||||
struct video_bridge_ops *ops = video_bridge_get_ops(dev); |
||||
int ret; |
||||
|
||||
if (!ops->check_attached) { |
||||
ret = dm_gpio_get_value(&uc_priv->hotplug); |
||||
|
||||
return ret > 0 ? 0 : ret == 0 ? -ENOTCONN : ret; |
||||
} |
||||
|
||||
return ops->check_attached(dev); |
||||
} |
||||
|
||||
static int video_bridge_pre_probe(struct udevice *dev) |
||||
{ |
||||
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); |
||||
int ret; |
||||
|
||||
debug("%s\n", __func__); |
||||
ret = gpio_request_by_name(dev, "sleep-gpios", 0, |
||||
&uc_priv->sleep, GPIOD_IS_OUT); |
||||
if (ret) { |
||||
debug("%s: Could not decode sleep-gpios (%d)\n", __func__, ret); |
||||
return ret; |
||||
} |
||||
/*
|
||||
* Drop this for now as we do not have driver model pinctrl support |
||||
* |
||||
* ret = dm_gpio_set_pull(&uc_priv->sleep, GPIO_PULL_NONE); |
||||
* if (ret) { |
||||
* debug("%s: Could not set sleep pull value\n", __func__); |
||||
* return ret; |
||||
* } |
||||
*/ |
||||
ret = gpio_request_by_name(dev, "reset-gpios", 0, &uc_priv->reset, |
||||
GPIOD_IS_OUT); |
||||
if (ret) { |
||||
debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret); |
||||
return ret; |
||||
} |
||||
/*
|
||||
* Drop this for now as we do not have driver model pinctrl support |
||||
* |
||||
* ret = dm_gpio_set_pull(&uc_priv->reset, GPIO_PULL_NONE); |
||||
* if (ret) { |
||||
* debug("%s: Could not set reset pull value\n", __func__); |
||||
* return ret; |
||||
* } |
||||
*/ |
||||
ret = gpio_request_by_name(dev, "hotplug-gpios", 0, &uc_priv->hotplug, |
||||
GPIOD_IS_IN); |
||||
if (ret && ret != -ENOENT) { |
||||
debug("%s: Could not decode hotplug (%d)\n", __func__, ret); |
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int video_bridge_set_active(struct udevice *dev, bool active) |
||||
{ |
||||
struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); |
||||
int ret; |
||||
|
||||
debug("%s: %d\n", __func__, active); |
||||
ret = dm_gpio_set_value(&uc_priv->sleep, !active); |
||||
if (ret) |
||||
return ret; |
||||
if (active) { |
||||
ret = dm_gpio_set_value(&uc_priv->reset, true); |
||||
if (ret) |
||||
return ret; |
||||
udelay(10); |
||||
ret = dm_gpio_set_value(&uc_priv->reset, false); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
UCLASS_DRIVER(video_bridge) = { |
||||
.id = UCLASS_VIDEO_BRIDGE, |
||||
.name = "video_bridge", |
||||
.per_device_auto_alloc_size = sizeof(struct video_bridge_priv), |
||||
.pre_probe = video_bridge_pre_probe, |
||||
}; |
@ -1,231 +0,0 @@ |
||||
/*
|
||||
* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
/*
|
||||
* This file is a driver for Parade dP<->LVDS bridges. The original submission |
||||
* is for the ps8625 chip. |
||||
*/ |
||||
#include <config.h> |
||||
#include <common.h> |
||||
#include <i2c.h> |
||||
#include <fdtdec.h> |
||||
#include <asm/gpio.h> |
||||
|
||||
/*
|
||||
* Initialization of the chip is a process of writing certaing values into |
||||
* certain registers over i2c bus. The chip in fact responds to a range of |
||||
* addresses on the i2c bus, so for each written value three parameters are |
||||
* required: i2c address, register address and the actual value. |
||||
* |
||||
* The base address is derived from the device tree, only address offset is |
||||
* stored in the table below. |
||||
*/ |
||||
/**
|
||||
* struct reg_data() - data for a parade register write |
||||
* |
||||
* @addr_off offset from the i2c base address for parade |
||||
* @reg_addr register address to write |
||||
* @value value to be written |
||||
*/ |
||||
struct reg_data { |
||||
uint8_t addr_off; |
||||
uint8_t reg; |
||||
uint8_t value; |
||||
} _packed; |
||||
|
||||
#define END_OF_TABLE 0xff /* Ficticious offset */ |
||||
|
||||
static const struct reg_data parade_values[] = { |
||||
{0x02, 0xa1, 0x01}, /* HPD low */ |
||||
/*
|
||||
* SW setting |
||||
* [1:0] SW output 1.2V voltage is lower to 96% |
||||
*/ |
||||
{0x04, 0x14, 0x01}, |
||||
/*
|
||||
* RCO SS setting |
||||
* [5:4] = b01 0.5%, b10 1%, b11 1.5% |
||||
*/ |
||||
{0x04, 0xe3, 0x20}, |
||||
{0x04, 0xe2, 0x80}, /* [7] RCO SS enable */ |
||||
/*
|
||||
* RPHY Setting |
||||
* [3:2] CDR tune wait cycle before |
||||
* measure for fine tune b00: 1us, |
||||
* 01: 0.5us, 10:2us, 11:4us. |
||||
*/ |
||||
{0x04, 0x8a, 0x0c}, |
||||
{0x04, 0x89, 0x08}, /* [3] RFD always on */ |
||||
/*
|
||||
* CTN lock in/out: |
||||
* 20000ppm/80000ppm. Lock out 2 |
||||
* times. |
||||
*/ |
||||
{0x04, 0x71, 0x2d}, |
||||
/*
|
||||
* 2.7G CDR settings |
||||
* NOF=40LSB for HBR CDR setting |
||||
*/ |
||||
{0x04, 0x7d, 0x07}, |
||||
{0x04, 0x7b, 0x00}, /* [1:0] Fmin=+4bands */ |
||||
{0x04, 0x7a, 0xfd}, /* [7:5] DCO_FTRNG=+-40% */ |
||||
/*
|
||||
* 1.62G CDR settings |
||||
* [5:2]NOF=64LSB [1:0]DCO scale is 2/5 |
||||
*/ |
||||
{0x04, 0xc0, 0x12}, |
||||
{0x04, 0xc1, 0x92}, /* Gitune=-37% */ |
||||
{0x04, 0xc2, 0x1c}, /* Fbstep=100% */ |
||||
{0x04, 0x32, 0x80}, /* [7] LOS signal disable */ |
||||
/*
|
||||
* RPIO Setting |
||||
* [7:4] LVDS driver bias current : |
||||
* 75% (250mV swing) |
||||
*/ |
||||
{0x04, 0x00, 0xb0}, |
||||
/*
|
||||
* [7:6] Right-bar GPIO output strength is 8mA |
||||
*/ |
||||
{0x04, 0x15, 0x40}, |
||||
/* EQ Training State Machine Setting */ |
||||
{0x04, 0x54, 0x10}, /* RCO calibration start */ |
||||
/* [4:0] MAX_LANE_COUNT set to one lane */ |
||||
{0x01, 0x02, 0x81}, |
||||
/* [4:0] LANE_COUNT_SET set to one lane */ |
||||
{0x01, 0x21, 0x81}, |
||||
{0x00, 0x52, 0x20}, |
||||
{0x00, 0xf1, 0x03}, /* HPD CP toggle enable */ |
||||
{0x00, 0x62, 0x41}, |
||||
/* Counter number, add 1ms counter delay */ |
||||
{0x00, 0xf6, 0x01}, |
||||
/*
|
||||
* [6]PWM function control by |
||||
* DPCD0040f[7], default is PWM |
||||
* block always works. |
||||
*/ |
||||
{0x00, 0x77, 0x06}, |
||||
/*
|
||||
* 04h Adjust VTotal tolerance to |
||||
* fix the 30Hz no display issue |
||||
*/ |
||||
{0x00, 0x4c, 0x04}, |
||||
/* DPCD00400='h00, Parade OUI = 'h001cf8 */ |
||||
{0x01, 0xc0, 0x00}, |
||||
{0x01, 0xc1, 0x1c}, /* DPCD00401='h1c */ |
||||
{0x01, 0xc2, 0xf8}, /* DPCD00402='hf8 */ |
||||
/*
|
||||
* DPCD403~408 = ASCII code |
||||
* D2SLV5='h4432534c5635 |
||||
*/ |
||||
{0x01, 0xc3, 0x44}, |
||||
{0x01, 0xc4, 0x32}, /* DPCD404 */ |
||||
{0x01, 0xc5, 0x53}, /* DPCD405 */ |
||||
{0x01, 0xc6, 0x4c}, /* DPCD406 */ |
||||
{0x01, 0xc7, 0x56}, /* DPCD407 */ |
||||
{0x01, 0xc8, 0x35}, /* DPCD408 */ |
||||
/*
|
||||
* DPCD40A, Initial Code major revision |
||||
* '01' |
||||
*/ |
||||
{0x01, 0xca, 0x01}, |
||||
/* DPCD40B, Initial Code minor revision '05' */ |
||||
{0x01, 0xcb, 0x05}, |
||||
/* DPCD720, Select internal PWM */ |
||||
{0x01, 0xa5, 0xa0}, |
||||
/*
|
||||
* FFh for 100% PWM of brightness, 0h for 0% |
||||
* brightness |
||||
*/ |
||||
{0x01, 0xa7, 0xff}, |
||||
/*
|
||||
* Set LVDS output as 6bit-VESA mapping, |
||||
* single LVDS channel |
||||
*/ |
||||
{0x01, 0xcc, 0x13}, |
||||
/* Enable SSC set by register */ |
||||
{0x02, 0xb1, 0x20}, |
||||
/*
|
||||
* Set SSC enabled and +/-1% central |
||||
* spreading |
||||
*/ |
||||
{0x04, 0x10, 0x16}, |
||||
/* MPU Clock source: LC => RCO */ |
||||
{0x04, 0x59, 0x60}, |
||||
{0x04, 0x54, 0x14}, /* LC -> RCO */ |
||||
{0x02, 0xa1, 0x91}, /* HPD high */ |
||||
{END_OF_TABLE} |
||||
}; |
||||
|
||||
/**
|
||||
* Write values table into the Parade eDP bridge |
||||
* |
||||
* @return 0 on success, non-0 on failure |
||||
*/ |
||||
|
||||
static int parade_write_regs(int base_addr, const struct reg_data *table) |
||||
{ |
||||
int ret = 0; |
||||
|
||||
while (!ret && (table->addr_off != END_OF_TABLE)) { |
||||
ret = i2c_write(base_addr + table->addr_off, |
||||
table->reg, 1, |
||||
(uint8_t *)&table->value, |
||||
sizeof(table->value)); |
||||
table++; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
int parade_init(const void *blob) |
||||
{ |
||||
struct gpio_desc rst_gpio; |
||||
struct gpio_desc slp_gpio; |
||||
int bus, old_bus; |
||||
int parent; |
||||
int node; |
||||
int addr; |
||||
int ret; |
||||
|
||||
node = fdtdec_next_compatible(blob, 0, COMPAT_PARADE_PS8625); |
||||
if (node < 0) |
||||
return 0; |
||||
|
||||
parent = fdt_parent_offset(blob, node); |
||||
if (parent < 0) { |
||||
debug("%s: Could not find parent i2c node\n", __func__); |
||||
return -1; |
||||
} |
||||
addr = fdtdec_get_int(blob, node, "reg", -1); |
||||
if (addr < 0) { |
||||
debug("%s: Could not find i2c address\n", __func__); |
||||
return -1; |
||||
} |
||||
|
||||
gpio_request_by_name_nodev(blob, node, "sleep-gpio", 0, &slp_gpio, |
||||
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); |
||||
|
||||
mdelay(10); |
||||
|
||||
gpio_request_by_name_nodev(blob, node, "reset-gpio", 0, &rst_gpio, |
||||
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); |
||||
|
||||
bus = i2c_get_bus_num_fdt(parent); |
||||
old_bus = i2c_get_bus_num(); |
||||
|
||||
debug("%s: Using i2c bus %d\n", __func__, bus); |
||||
|
||||
/*
|
||||
* TODO(sjg@chromium.org): Hmmm we seem to need some sort of delay |
||||
* here. |
||||
*/ |
||||
mdelay(40); |
||||
i2c_set_bus_num(bus); |
||||
ret = parade_write_regs(addr, parade_values); |
||||
|
||||
i2c_set_bus_num(old_bus); |
||||
|
||||
return ret; |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue