diff options
Diffstat (limited to 'sys/arm/nvidia/tegra_pinmux.c')
-rw-r--r-- | sys/arm/nvidia/tegra_pinmux.c | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/sys/arm/nvidia/tegra_pinmux.c b/sys/arm/nvidia/tegra_pinmux.c new file mode 100644 index 0000000..6123746 --- /dev/null +++ b/sys/arm/nvidia/tegra_pinmux.c @@ -0,0 +1,804 @@ +/*- + * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Pin multiplexer driver for Tegra SoCs. + */ +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/malloc.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/fdt/fdt_pinctrl.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +/* Pin multipexor register. */ +#define TEGRA_MUX_FUNCTION_MASK 0x03 +#define TEGRA_MUX_FUNCTION_SHIFT 0 +#define TEGRA_MUX_PUPD_MASK 0x03 +#define TEGRA_MUX_PUPD_SHIFT 2 +#define TEGRA_MUX_TRISTATE_SHIFT 4 +#define TEGRA_MUX_ENABLE_INPUT_SHIFT 5 +#define TEGRA_MUX_OPEN_DRAIN_SHIFT 6 +#define TEGRA_MUX_LOCK_SHIFT 7 +#define TEGRA_MUX_IORESET_SHIFT 8 +#define TEGRA_MUX_RCV_SEL_SHIFT 9 + + +/* Pin goup register. */ +#define TEGRA_GRP_HSM_SHIFT 2 +#define TEGRA_GRP_SCHMT_SHIFT 3 +#define TEGRA_GRP_DRV_TYPE_SHIFT 6 +#define TEGRA_GRP_DRV_TYPE_MASK 0x03 +#define TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT 28 +#define TEGRA_GRP_DRV_DRVDN_SLWR_MASK 0x03 +#define TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT 30 +#define TEGRA_GRP_DRV_DRVUP_SLWF_MASK 0x03 + +struct pinmux_softc { + device_t dev; + struct resource *pad_mem_res; + struct resource *mux_mem_res; + struct resource *mipi_mem_res; +}; + +static struct ofw_compat_data compat_data[] = { + {"nvidia,tegra124-pinmux", 1}, + {NULL, 0}, +}; + +enum prop_id { + PROP_ID_PULL, + PROP_ID_TRISTATE, + PROP_ID_ENABLE_INPUT, + PROP_ID_OPEN_DRAIN, + PROP_ID_LOCK, + PROP_ID_IORESET, + PROP_ID_RCV_SEL, + PROP_ID_HIGH_SPEED_MODE, + PROP_ID_SCHMITT, + PROP_ID_LOW_POWER_MODE, + PROP_ID_DRIVE_DOWN_STRENGTH, + PROP_ID_DRIVE_UP_STRENGTH, + PROP_ID_SLEW_RATE_FALLING, + PROP_ID_SLEW_RATE_RISING, + PROP_ID_DRIVE_TYPE, + + PROP_ID_MAX_ID +}; + +/* Numeric based parameters. */ +static const struct prop_name { + const char *name; + enum prop_id id; +} prop_names[] = { + {"nvidia,pull", PROP_ID_PULL}, + {"nvidia,tristate", PROP_ID_TRISTATE}, + {"nvidia,enable-input", PROP_ID_ENABLE_INPUT}, + {"nvidia,open-drain", PROP_ID_OPEN_DRAIN}, + {"nvidia,lock", PROP_ID_LOCK}, + {"nvidia,io-reset", PROP_ID_IORESET}, + {"nvidia,rcv-sel", PROP_ID_RCV_SEL}, + {"nvidia,high-speed-mode", PROP_ID_HIGH_SPEED_MODE}, + {"nvidia,schmitt", PROP_ID_SCHMITT}, + {"nvidia,low-power-mode", PROP_ID_LOW_POWER_MODE}, + {"nvidia,pull-down-strength", PROP_ID_DRIVE_DOWN_STRENGTH}, + {"nvidia,pull-up-strength", PROP_ID_DRIVE_UP_STRENGTH}, + {"nvidia,slew-rate-falling", PROP_ID_SLEW_RATE_FALLING}, + {"nvidia,slew-rate-rising", PROP_ID_SLEW_RATE_RISING}, + {"nvidia,drive-type", PROP_ID_DRIVE_TYPE}, +}; + +/* + * configuration for one pin group. + */ +struct pincfg { + char *function; + int params[PROP_ID_MAX_ID]; +}; +#define GPIO_BANK_A 0 +#define GPIO_BANK_B 1 +#define GPIO_BANK_C 2 +#define GPIO_BANK_D 3 +#define GPIO_BANK_E 4 +#define GPIO_BANK_F 5 +#define GPIO_BANK_G 6 +#define GPIO_BANK_H 7 +#define GPIO_BANK_I 8 +#define GPIO_BANK_J 9 +#define GPIO_BANK_K 10 +#define GPIO_BANK_L 11 +#define GPIO_BANK_M 12 +#define GPIO_BANK_N 13 +#define GPIO_BANK_O 14 +#define GPIO_BANK_P 15 +#define GPIO_BANK_Q 16 +#define GPIO_BANK_R 17 +#define GPIO_BANK_S 18 +#define GPIO_BANK_T 19 +#define GPIO_BANK_U 20 +#define GPIO_BANK_V 21 +#define GPIO_BANK_W 22 +#define GPIO_BANK_X 23 +#define GPIO_BANK_Y 24 +#define GPIO_BANK_Z 25 +#define GPIO_BANK_AA 26 +#define GPIO_BANK_BB 27 +#define GPIO_BANK_CC 28 +#define GPIO_BANK_DD 29 +#define GPIO_BANK_EE 30 +#define GPIO_BANK_FF 31 +#define GPIO_NUM(b, p) (8 * (b) + (p)) + +struct tegra_mux { + char *name; + bus_size_t reg; + char *functions[4]; + int gpio_num; +}; + +#define GMUX(r, gb, gi, nm, f1, f2, f3, f4) \ +{ \ + .name = #nm, \ + .reg = r, \ + .gpio_num = GPIO_NUM(GPIO_BANK_##gb, gi), \ + .functions = {#f1, #f2, #f3, #f4}, \ +} + +#define FMUX(r, nm, f1, f2, f3, f4) \ +{ \ + .name = #nm, \ + .reg = r, \ + .gpio_num = -1, \ + .functions = {#f1, #f2, #f3, #f4}, \ +} + +static const struct tegra_mux pin_mux_tbl[] = { + GMUX(0x000, O, 1, ulpi_data0_po1, spi3, hsi, uarta, ulpi), + GMUX(0x004, O, 2, ulpi_data1_po2, spi3, hsi, uarta, ulpi), + GMUX(0x008, O, 3, ulpi_data2_po3, spi3, hsi, uarta, ulpi), + GMUX(0x00C, O, 4, ulpi_data3_po4, spi3, hsi, uarta, ulpi), + GMUX(0x010, O, 5, ulpi_data4_po5, spi2, hsi, uarta, ulpi), + GMUX(0x014, O, 6, ulpi_data5_po6, spi2, hsi, uarta, ulpi), + GMUX(0x018, O, 7, ulpi_data6_po7, spi2, hsi, uarta, ulpi), + GMUX(0x01C, O, 0, ulpi_data7_po0, spi2, hsi, uarta, ulpi), + GMUX(0x020, P, 9, ulpi_clk_py0, spi1, spi5, uartd, ulpi), + GMUX(0x024, P, 1, ulpi_dir_py1, spi1, spi5, uartd, ulpi), + GMUX(0x028, P, 2, ulpi_nxt_py2, spi1, spi5, uartd, ulpi), + GMUX(0x02C, P, 3, ulpi_stp_py3, spi1, spi5, uartd, ulpi), + GMUX(0x030, P, 0, dap3_fs_pp0, i2s2, spi5, displaya, displayb), + GMUX(0x034, P, 1, dap3_din_pp1, i2s2, spi5, displaya, displayb), + GMUX(0x038, P, 2, dap3_dout_pp2, i2s2, spi5, displaya, rsvd4), + GMUX(0x03C, P, 3, dap3_sclk_pp3, i2s2, spi5, rsvd3, displayb), + GMUX(0x040, V, 0, pv0, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x044, V, 1, pv1, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x048, Z, 0, sdmmc1_clk_pz0, sdmmc1, clk12, rsvd3, rsvd4), + GMUX(0x04C, Z, 1, sdmmc1_cmd_pz1, sdmmc1, spdif, spi4, uarta), + GMUX(0x050, Y, 4, sdmmc1_dat3_py4, sdmmc1, spdif, spi4, uarta), + GMUX(0x054, Y, 5, sdmmc1_dat2_py5, sdmmc1, pwm0, spi4, uarta), + GMUX(0x058, Y, 6, sdmmc1_dat1_py6, sdmmc1, pwm1, spi4, uarta), + GMUX(0x05C, Y, 7, sdmmc1_dat0_py7, sdmmc1, rsvd2, spi4, uarta), + GMUX(0x068, W, 5, clk2_out_pw5, extperiph2, rsvd2, rsvd3, rsvd4), + GMUX(0x06C, CC, 5, clk2_req_pcc5, dap, rsvd2, rsvd3, rsvd4), + GMUX(0x110, N, 7, hdmi_int_pn7, rsvd1, rsvd2, rsvd3, rsvd4), + GMUX(0x114, V, 4, ddc_scl_pv4, i2c4, rsvd2, rsvd3, rsvd4), + GMUX(0x118, V, 5, ddc_sda_pv5, i2c4, rsvd2, rsvd3, rsvd4), + GMUX(0x164, V, 3, uart2_rxd_pc3, irda, spdif, uarta, spi4), + GMUX(0x168, C, 2, uart2_txd_pc2, irda, spdif, uarta, spi4), + GMUX(0x16C, J, 6, uart2_rts_n_pj6, uarta, uartb, gmi, spi4), + GMUX(0x170, J, 5, uart2_cts_n_pj5, uarta, uartb, gmi, spi4), + GMUX(0x174, W, 6, uart3_txd_pw6, uartc, rsvd2, gmi, spi4), + GMUX(0x178, W, 7, uart3_rxd_pw7, uartc, rsvd2, gmi, spi4), + GMUX(0x17C, S, 1, uart3_cts_n_pa1, uartc, sdmmc1, dtv, gmi), + GMUX(0x180, C, 0, uart3_rts_n_pc0, uartc, pwm0, dtv, gmi), + GMUX(0x184, U, 0, pu0, owr, uarta, gmi, rsvd4), + GMUX(0x188, U, 1, pu1, rsvd1, uarta, gmi, rsvd4), + GMUX(0x18C, U, 2, pu2, rsvd1, uarta, gmi, rsvd4), + GMUX(0x190, U, 3, pu3, pwm0, uarta, gmi, displayb), + GMUX(0x194, U, 4, pu4, pwm1, uarta, gmi, displayb), + GMUX(0x198, U, 5, pu5, pwm2, uarta, gmi, displayb), + GMUX(0x19C, U, 6, pu6, pwm3, uarta, rsvd3, gmi), + GMUX(0x1A0, C, 5, gen1_i2c_sda_pc5, i2c1, rsvd2, rsvd3, rsvd4), + GMUX(0x1A4, C, 4, gen1_i2c_scl_pc4, i2c1, rsvd2, rsvd3, rsvd4), + GMUX(0x1A8, P, 3, dap4_fs_pp4, i2s3, gmi, dtv, rsvd4), + GMUX(0x1AC, P, 4, dap4_din_pp5, i2s3, gmi, rsvd3, rsvd4), + GMUX(0x1B0, P, 5, dap4_dout_pp6, i2s3, gmi, dtv, rsvd4), + GMUX(0x1B4, P, 7, dap4_sclk_pp7, i2s3, gmi, rsvd3, rsvd4), + GMUX(0x1B8, P, 0, clk3_out_pee0, extperiph3, rsvd2, rsvd3, rsvd4), + GMUX(0x1BC, EE, 1, clk3_req_pee1, dev3, rsvd2, rsvd3, rsvd4), + GMUX(0x1C0, C, 7, pc7, rsvd1, rsvd2, gmi, gmi_alt), + GMUX(0x1C4, I, 5, pi5, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x1C8, I, 7, pi7, rsvd1, trace, gmi, dtv), + GMUX(0x1CC, K, 0, pk0, rsvd1, sdmmc3, gmi, soc), + GMUX(0x1D0, K, 1, pk1, sdmmc2, trace, gmi, rsvd4), + GMUX(0x1D4, J, 0, pj0, rsvd1, rsvd2, gmi, usb), + GMUX(0x1D8, J, 2, pj2, rsvd1, rsvd2, gmi, soc), + GMUX(0x1DC, K, 3, pk3, sdmmc2, trace, gmi, ccla), + GMUX(0x1E0, K, 4, pk4, sdmmc2, rsvd2, gmi, gmi_alt), + GMUX(0x1E4, K, 2, pk2, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1E8, I, 3, pi3, rsvd1, rsvd2, gmi, spi4), + GMUX(0x1EC, I, 6, pi6, rsvd1, rsvd2, gmi, sdmmc2), + GMUX(0x1F0, G, 0, pg0, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1F4, G, 1, pg1, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x1F8, G, 2, pg2, rsvd1, trace, gmi, rsvd4), + GMUX(0x1FC, G, 3, pg3, rsvd1, trace, gmi, rsvd4), + GMUX(0x200, G, 4, pg4, rsvd1, tmds, gmi, spi4), + GMUX(0x204, G, 5, pg5, rsvd1, rsvd2, gmi, spi4), + GMUX(0x208, G, 6, pg6, rsvd1, rsvd2, gmi, spi4), + GMUX(0x20C, G, 7, pg7, rsvd1, rsvd2, gmi, spi4), + GMUX(0x210, H, 0, ph0, pwm0, trace, gmi, dtv), + GMUX(0x214, H, 1, ph1, pwm1, tmds, gmi, displaya), + GMUX(0x218, H, 2, ph2, pwm2, tmds, gmi, cldvfs), + GMUX(0x21C, H, 3, ph3, pwm3, spi4, gmi, cldvfs), + GMUX(0x220, H, 4, ph4, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x224, H, 5, ph5, sdmmc2, rsvd2, gmi, rsvd4), + GMUX(0x228, H, 6, ph6, sdmmc2, trace, gmi, dtv), + GMUX(0x22C, H, 7, ph7, sdmmc2, trace, gmi, dtv), + GMUX(0x230, J, 7, pj7, uartd, rsvd2, gmi, gmi_alt), + GMUX(0x234, B, 0, pb0, uartd, rsvd2, gmi, rsvd4), + GMUX(0x238, B, 1, pb1, uartd, rsvd2, gmi, rsvd4), + GMUX(0x23C, K, 7, pk7, uartd, rsvd2, gmi, rsvd4), + GMUX(0x240, I, 0, pi0, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x244, I, 1, pi1, rsvd1, rsvd2, gmi, rsvd4), + GMUX(0x248, I, 2, pi2, sdmmc2, trace, gmi, rsvd4), + GMUX(0x24C, I, 4, pi4, spi4, trace, gmi, displaya), + GMUX(0x250, T, 5, gen2_i2c_scl_pt5, i2c2, rsvd2, gmi, rsvd4), + GMUX(0x254, T, 6, gen2_i2c_sda_pt6, i2c2, rsvd2, gmi, rsvd4), + GMUX(0x258, CC, 4, sdmmc4_clk_pcc4, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x25C, T, 7, sdmmc4_cmd_pt7, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x260, AA, 0, sdmmc4_dat0_paa0, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x264, AA, 1, sdmmc4_dat1_paa1, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x268, AA, 2, sdmmc4_dat2_paa2, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x26C, AA, 3, sdmmc4_dat3_paa3, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x270, AA, 4, sdmmc4_dat4_paa4, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x274, AA, 5, sdmmc4_dat5_paa5, sdmmc4, spi3, rsvd3, rsvd4), + GMUX(0x278, AA, 6, sdmmc4_dat6_paa6, sdmmc4, spi3, gmi, rsvd4), + GMUX(0x27C, AA, 7, sdmmc4_dat7_paa7, sdmmc4, rsvd2, gmi, rsvd4), + GMUX(0x284, CC, 0, cam_mclk_pcc0, vi, vi_alt1, vi_alt3, sdmmc2), + GMUX(0x288, CC, 1, pcc1, i2s4, rsvd2, rsvd3, sdmmc2), + GMUX(0x28C, BB, 0, pbb0, vgp6, vimclk2, sdmmc2, vimclk2_alt), + GMUX(0x290, BB, 1, cam_i2c_scl_pbb1, vgp1, i2c3, rsvd3, sdmmc2), + GMUX(0x294, BB, 2, cam_i2c_sda_pbb2, vgp2, i2c3, rsvd3, sdmmc2), + GMUX(0x298, BB, 3, pbb3, vgp3, displaya, displayb, sdmmc2), + GMUX(0x29C, BB, 4, pbb4, vgp4, displaya, displayb, sdmmc2), + GMUX(0x2A0, BB, 5, pbb5, vgp5, displaya, rsvd3, sdmmc2), + GMUX(0x2A4, BB, 6, pbb6, i2s4, rsvd2, displayb, sdmmc2), + GMUX(0x2A8, BB, 7, pbb7, i2s4, rsvd2, rsvd3, sdmmc2), + GMUX(0x2AC, CC, 2, pcc2, i2s4, rsvd2, sdmmc3, sdmmc2), + FMUX(0x2B0, jtag_rtck, rtck, rsvd2, rsvd3, rsvd4), + GMUX(0x2B4, Z, 6, pwr_i2c_scl_pz6, i2cpwr, rsvd2, rsvd3, rsvd4), + GMUX(0x2B8, Z, 7, pwr_i2c_sda_pz7, i2cpwr, rsvd2, rsvd3, rsvd4), + GMUX(0x2BC, R, 0, kb_row0_pr0, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C0, R, 1, kb_row1_pr1, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C4, R, 2, kb_row2_pr2, kbc, rsvd2, rsvd3, rsvd4), + GMUX(0x2C8, R, 3, kb_row3_pr3, kbc, displaya, sys, displayb), + GMUX(0x2CC, R, 4, kb_row4_pr4, kbc, displaya, rsvd3, displayb), + GMUX(0x2D0, R, 5, kb_row5_pr5, kbc, displaya, rsvd3, displayb), + GMUX(0x2D4, R, 6, kb_row6_pr6, kbc, displaya, displaya_alt, displayb), + GMUX(0x2D8, R, 7, kb_row7_pr7, kbc, rsvd2, cldvfs, uarta), + GMUX(0x2DC, S, 0, kb_row8_ps0, kbc, rsvd2, cldvfs, uarta), + GMUX(0x2E0, S, 1, kb_row9_ps1, kbc, rsvd2, rsvd3, uarta), + GMUX(0x2E4, S, 2, kb_row10_ps2, kbc, rsvd2, rsvd3, uarta), + GMUX(0x2E8, S, 3, kb_row11_ps3, kbc, rsvd2, rsvd3, irda), + GMUX(0x2EC, S, 4, kb_row12_ps4, kbc, rsvd2, rsvd3, irda), + GMUX(0x2F0, S, 5, kb_row13_ps5, kbc, rsvd2, spi2, rsvd4), + GMUX(0x2F4, S, 6, kb_row14_ps6, kbc, rsvd2, spi2, rsvd4), + GMUX(0x2F8, S, 7, kb_row15_ps7, kbc, soc, rsvd3, rsvd4), + GMUX(0x2FC, Q, 0, kb_col0_pq0, kbc, rsvd2, spi2, rsvd4), + GMUX(0x300, Q, 1, kb_col1_pq1, kbc, rsvd2, spi2, rsvd4), + GMUX(0x304, Q, 2, kb_col2_pq2, kbc, rsvd2, spi2, rsvd4), + GMUX(0x308, Q, 3, kb_col3_pq3, kbc, displaya, pwm2, uarta), + GMUX(0x30C, Q, 4, kb_col4_pq4, kbc, owr, sdmmc3, uarta), + GMUX(0x310, Q, 5, kb_col5_pq5, kbc, rsvd2, sdmmc3, rsvd4), + GMUX(0x314, Q, 6, kb_col6_pq6, kbc, rsvd2, spi2, uartd), + GMUX(0x318, Q, 7, kb_col7_pq7, kbc, rsvd2, spi2, uartd), + GMUX(0x31C, A, 0, clk_32k_out_pa0, blink, soc, rsvd3, rsvd4), + FMUX(0x324, core_pwr_req, pwron, rsvd2, rsvd3, rsvd4), + FMUX(0x328, cpu_pwr_req, cpu, rsvd2, rsvd3, rsvd4), + FMUX(0x32C, pwr_int_n, pmi, rsvd2, rsvd3, rsvd4), + FMUX(0x330, clk_32k_in, clk, rsvd2, rsvd3, rsvd4), + FMUX(0x334, owr, owr, rsvd2, rsvd3, rsvd4), + GMUX(0x338, N, 0, dap1_fs_pn0, i2s0, hda, gmi, rsvd4), + GMUX(0x33C, N, 1, dap1_din_pn1, i2s0, hda, gmi, rsvd4), + GMUX(0x340, N, 2, dap1_dout_pn2, i2s0, hda, gmi, sata), + GMUX(0x344, N, 3, dap1_sclk_pn3, i2s0, hda, gmi, rsvd4), + GMUX(0x348, EE, 2, dap_mclk1_req_pee2, dap, dap1, sata, rsvd4), + GMUX(0x34C, W, 4, dap_mclk1_pw4, extperiph1, dap2, rsvd3, rsvd4), + GMUX(0x350, K, 6, spdif_in_pk6, spdif, rsvd2, rsvd3, i2c3), + GMUX(0x354, K, 5, spdif_out_pk5, spdif, rsvd2, rsvd3, i2c3), + GMUX(0x358, A, 2, dap2_fs_pa2, i2s1, hda, gmi, rsvd4), + GMUX(0x35C, A, 4, dap2_din_pa4, i2s1, hda, gmi, rsvd4), + GMUX(0x360, A, 5, dap2_dout_pa5, i2s1, hda, gmi, rsvd4), + GMUX(0x364, A, 3, dap2_sclk_pa3, i2s1, hda, gmi, rsvd4), + GMUX(0x368, X, 0, dvfs_pwm_px0, spi6, cldvfs, gmi, rsvd4), + GMUX(0x36C, X, 1, gpio_x1_aud_px1, spi6, rsvd2, gmi, rsvd4), + GMUX(0x370, X, 3, gpio_x3_aud_px3, spi6, spi1, gmi, rsvd4), + GMUX(0x374, X, 2, dvfs_clk_px2, spi6, cldvfs, gmi, rsvd4), + GMUX(0x378, X, 4, gpio_x4_aud_px4, gmi, spi1, spi2, dap2), + GMUX(0x37C, X, 5, gpio_x5_aud_px5, gmi, spi1, spi2, rsvd4), + GMUX(0x380, X, 6, gpio_x6_aud_px6, spi6, spi1, spi2, gmi), + GMUX(0x384, X, 7, gpio_x7_aud_px7, rsvd1, spi1, spi2, rsvd4), + GMUX(0x390, A, 6, sdmmc3_clk_pa6, sdmmc3, rsvd2, rsvd3, spi3), + GMUX(0x394, A, 7, sdmmc3_cmd_pa7, sdmmc3, pwm3, uarta, spi3), + GMUX(0x398, B, 7, sdmmc3_dat0_pb7, sdmmc3, rsvd2, rsvd3, spi3), + GMUX(0x39C, B, 6, sdmmc3_dat1_pb6, sdmmc3, pwm2, uarta, spi3), + GMUX(0x3A0, B, 5, sdmmc3_dat2_pb5, sdmmc3, pwm1, displaya, spi3), + GMUX(0x3A4, B, 4, sdmmc3_dat3_pb4, sdmmc3, pwm0, displayb, spi3), + GMUX(0x3BC, DD, 1, pex_l0_rst_n_pdd1, pe0, rsvd2, rsvd3, rsvd4), + GMUX(0x3C0, DD, 2, pex_l0_clkreq_n_pdd2, pe0, rsvd2, rsvd3, rsvd4), + GMUX(0x3C4, DD, 3, pex_wake_n_pdd3, pe, rsvd2, rsvd3, rsvd4), + GMUX(0x3CC, DD, 5, pex_l1_rst_n_pdd5, pe1, rsvd2, rsvd3, rsvd4), + GMUX(0x3D0, DD, 6, pex_l1_clkreq_n_pdd6, pe1, rsvd2, rsvd3, rsvd4), + GMUX(0x3E0, EE, 3, hdmi_cec_pee3, cec, rsvd2, rsvd3, rsvd4), + GMUX(0x3E4, V, 3, sdmmc1_wp_n_pv3, sdmmc1, clk12, spi4, uarta), + GMUX(0x3E8, V, 2, sdmmc3_cd_n_pv2, sdmmc3, owr, rsvd3, rsvd4), + GMUX(0x3EC, W, 2, gpio_w2_aud_pw2, spi6, rsvd2, spi2, i2c1), + GMUX(0x3F0, W, 3, gpio_w3_aud_pw3, spi6, spi1, spi2, i2c1), + GMUX(0x3F4, N, 4, usb_vbus_en0_pn4, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x3F8, N, 5, usb_vbus_en1_pn5, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x3FC, EE, 5, sdmmc3_clk_lb_in_pee5, sdmmc3, rsvd2, rsvd3, rsvd4), + GMUX(0x400, EE, 4, sdmmc3_clk_lb_out_pee4, sdmmc3, rsvd2, rsvd3, rsvd4), + FMUX(0x404, gmi_clk_lb, sdmmc2, rsvd2, gmi, rsvd4), + FMUX(0x408, reset_out_n, rsvd1, rsvd2, rsvd3, reset_out_n), + GMUX(0x40C, T, 0, kb_row16_pt0, kbc, rsvd2, rsvd3, uartc), + GMUX(0x410, T, 1, kb_row17_pt1, kbc, rsvd2, rsvd3, uartc), + GMUX(0x414, FF, 1, usb_vbus_en2_pff1, usb, rsvd2, rsvd3, rsvd4), + GMUX(0x418, FF, 2, pff2, sata, rsvd2, rsvd3, rsvd4), + GMUX(0x430, FF, 0, dp_hpd_pff0, dp, rsvd2, rsvd3, rsvd4), +}; + +struct tegra_grp { + char *name; + bus_size_t reg; + int drvdn_shift; + int drvdn_mask; + int drvup_shift; + int drvup_mask; +}; + +#define GRP(r, nm, dn_s, dn_w, up_s, up_w) \ +{ \ + .name = #nm, \ + .reg = r - 0x868, \ + .drvdn_shift = dn_s, \ + .drvdn_mask = (1 << dn_w) - 1, \ + .drvup_shift = up_s, \ + .drvup_mask = (1 << dn_w) - 1, \ +} + +/* Use register offsets from TRM */ +static const struct tegra_grp pin_grp_tbl[] = { + GRP(0x868, ao1, 12, 5, 20, 5), + GRP(0x86C, ao2, 12, 5, 20, 5), + GRP(0x870, at1, 12, 7, 20, 7), + GRP(0x874, at2, 12, 7, 20, 7), + GRP(0x878, at3, 12, 7, 20, 7), + GRP(0x87C, at4, 12, 7, 20, 7), + GRP(0x880, at5, 14, 5, 19, 5), + GRP(0x884, cdev1, 12, 5, 20, 5), + GRP(0x888, cdev2, 12, 5, 20, 5), + GRP(0x890, dap1, 12, 5, 20, 5), + GRP(0x894, dap2, 12, 5, 20, 5), + GRP(0x898, dap3, 12, 5, 20, 5), + GRP(0x89C, dap4, 12, 5, 20, 5), + GRP(0x8A0, dbg, 12, 5, 20, 5), + GRP(0x8B0, sdio3, 12, 7, 20, 7), + GRP(0x8B4, spi, 12, 5, 20, 5), + GRP(0x8B8, uaa, 12, 5, 20, 5), + GRP(0x8BC, uab, 12, 5, 20, 5), + GRP(0x8C0, uart2, 12, 5, 20, 5), + GRP(0x8C4, uart3, 12, 5, 20, 5), + GRP(0x8EC, sdio1, 12, 7, 20, 7), + GRP(0x8FC, ddc, 12, 5, 20, 5), + GRP(0x900, gma, 14, 5, 20, 5), + GRP(0x910, gme, 14, 5, 19, 5), + GRP(0x914, gmf, 14, 5, 19, 5), + GRP(0x918, gmg, 14, 5, 19, 5), + GRP(0x91C, gmh, 14, 5, 19, 5), + GRP(0x920, owr, 12, 5, 20, 5), + GRP(0x924, uda, 12, 5, 20, 5), + GRP(0x928, gpv, 12, 5, 20, 5), + GRP(0x92C, dev3, 12, 5, 20, 5), + GRP(0x938, cec, 12, 5, 20, 5), + GRP(0x994, at6, 12, 7, 20, 7), + GRP(0x998, dap5, 12, 5, 20, 5), + GRP(0x99C, usb_vbus_en, 12, 5, 20, 5), + GRP(0x9A8, ao3, 12, 5, -1, 0), + GRP(0x9B0, ao0, 12, 5, 20, 5), + GRP(0x9B4, hv0, 12, 5, -1, 0), + GRP(0x9C4, sdio4, 12, 5, 20, 5), + GRP(0x9C8, ao4, 12, 7, 20, 7), +}; + +static const struct tegra_grp * +pinmux_search_grp(char *grp_name) +{ + int i; + + for (i = 0; i < nitems(pin_grp_tbl); i++) { + if (strcmp(grp_name, pin_grp_tbl[i].name) == 0) + return (&pin_grp_tbl[i]); + } + return (NULL); +} + +static const struct tegra_mux * +pinmux_search_mux(char *pin_name) +{ + int i; + + for (i = 0; i < nitems(pin_mux_tbl); i++) { + if (strcmp(pin_name, pin_mux_tbl[i].name) == 0) + return (&pin_mux_tbl[i]); + } + return (NULL); +} + +static int +pinmux_mux_function(const struct tegra_mux *mux, char *fnc_name) +{ + int i; + + for (i = 0; i < 4; i++) { + if (strcmp(fnc_name, mux->functions[i]) == 0) + return (i); + } + return (-1); +} + +static int +pinmux_config_mux(struct pinmux_softc *sc, char *pin_name, + const struct tegra_mux *mux, struct pincfg *cfg) +{ + int tmp; + uint32_t reg; + + reg = bus_read_4(sc->mux_mem_res, mux->reg); + + if (cfg->function != NULL) { + tmp = pinmux_mux_function(mux, cfg->function); + if (tmp == -1) { + device_printf(sc->dev, + "Unknown function %s for pin %s\n", cfg->function, + pin_name); + return (ENXIO); + } + reg &= ~(TEGRA_MUX_FUNCTION_MASK << TEGRA_MUX_FUNCTION_SHIFT); + reg |= (tmp & TEGRA_MUX_FUNCTION_MASK) << + TEGRA_MUX_FUNCTION_SHIFT; + } + if (cfg->params[PROP_ID_PULL] != -1) { + reg &= ~(TEGRA_MUX_PUPD_MASK << TEGRA_MUX_PUPD_SHIFT); + reg |= (cfg->params[PROP_ID_PULL] & TEGRA_MUX_PUPD_MASK) << + TEGRA_MUX_PUPD_SHIFT; + } + if (cfg->params[PROP_ID_TRISTATE] != -1) { + reg &= ~(1 << TEGRA_MUX_TRISTATE_SHIFT); + reg |= (cfg->params[PROP_ID_TRISTATE] & 1) << + TEGRA_MUX_TRISTATE_SHIFT; + } + if (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[TEGRA_MUX_ENABLE_INPUT_SHIFT] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[PROP_ID_ENABLE_INPUT] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_ENABLE_INPUT] != -1) { + reg &= ~(1 << TEGRA_MUX_ENABLE_INPUT_SHIFT); + reg |= (cfg->params[PROP_ID_OPEN_DRAIN] & 1) << + TEGRA_MUX_ENABLE_INPUT_SHIFT; + } + if (cfg->params[PROP_ID_LOCK] != -1) { + reg &= ~(1 << TEGRA_MUX_LOCK_SHIFT); + reg |= (cfg->params[PROP_ID_LOCK] & 1) << + TEGRA_MUX_LOCK_SHIFT; + } + if (cfg->params[PROP_ID_IORESET] != -1) { + reg &= ~(1 << TEGRA_MUX_IORESET_SHIFT); + reg |= (cfg->params[PROP_ID_IORESET] & 1) << + TEGRA_MUX_IORESET_SHIFT; + } + if (cfg->params[PROP_ID_RCV_SEL] != -1) { + reg &= ~(1 << TEGRA_MUX_RCV_SEL_SHIFT); + reg |= (cfg->params[PROP_ID_RCV_SEL] & 1) << + TEGRA_MUX_RCV_SEL_SHIFT; + } + bus_write_4(sc->mux_mem_res, mux->reg, reg); + return (0); +} + +static int +pinmux_config_grp(struct pinmux_softc *sc, char *grp_name, + const struct tegra_grp *grp, struct pincfg *cfg) +{ + uint32_t reg; + + reg = bus_read_4(sc->pad_mem_res, grp->reg); + + if (cfg->params[PROP_ID_HIGH_SPEED_MODE] != -1) { + reg &= ~(1 << TEGRA_GRP_HSM_SHIFT); + reg |= (cfg->params[PROP_ID_HIGH_SPEED_MODE] & 1) << + TEGRA_GRP_HSM_SHIFT; + } + if (cfg->params[PROP_ID_SCHMITT] != -1) { + reg &= ~(1 << TEGRA_GRP_SCHMT_SHIFT); + reg |= (cfg->params[PROP_ID_SCHMITT] & 1) << + TEGRA_GRP_SCHMT_SHIFT; + } + if (cfg->params[PROP_ID_DRIVE_TYPE] != -1) { + reg &= ~(TEGRA_GRP_DRV_TYPE_MASK << TEGRA_GRP_DRV_TYPE_SHIFT); + reg |= (cfg->params[PROP_ID_DRIVE_TYPE] & + TEGRA_GRP_DRV_TYPE_MASK) << TEGRA_GRP_DRV_TYPE_SHIFT; + } + if (cfg->params[PROP_ID_SLEW_RATE_RISING] != -1) { + reg &= ~(TEGRA_GRP_DRV_DRVDN_SLWR_MASK << + TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT); + reg |= (cfg->params[PROP_ID_SLEW_RATE_RISING] & + TEGRA_GRP_DRV_DRVDN_SLWR_MASK) << + TEGRA_GRP_DRV_DRVDN_SLWR_SHIFT; + } + if (cfg->params[PROP_ID_SLEW_RATE_FALLING] != -1) { + reg &= ~(TEGRA_GRP_DRV_DRVUP_SLWF_MASK << + TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT); + reg |= (cfg->params[PROP_ID_SLEW_RATE_FALLING] & + TEGRA_GRP_DRV_DRVUP_SLWF_MASK) << + TEGRA_GRP_DRV_DRVUP_SLWF_SHIFT; + } + if ((cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] != -1) && + (grp->drvdn_mask != -1)) { + reg &= ~(grp->drvdn_shift << grp->drvdn_mask); + reg |= (cfg->params[PROP_ID_DRIVE_DOWN_STRENGTH] & + grp->drvdn_mask) << grp->drvdn_shift; + } + if ((cfg->params[PROP_ID_DRIVE_UP_STRENGTH] != -1) && + (grp->drvup_mask != -1)) { + reg &= ~(grp->drvup_shift << grp->drvup_mask); + reg |= (cfg->params[PROP_ID_DRIVE_UP_STRENGTH] & + grp->drvup_mask) << grp->drvup_shift; + } + bus_write_4(sc->pad_mem_res, grp->reg, reg); + return (0); +} + +static int +pinmux_config_node(struct pinmux_softc *sc, char *pin_name, struct pincfg *cfg) +{ + const struct tegra_mux *mux; + const struct tegra_grp *grp; + uint32_t reg; + int rv; + + /* Handle MIPI special case first */ + if (strcmp(pin_name, "dsi_b") == 0) { + if (cfg->function == NULL) { + /* nothing to set */ + return (0); + } + reg = bus_read_4(sc->mipi_mem_res, 0); /* register 0x820 */ + if (strcmp(cfg->function, "csi") == 0) + reg &= ~(1 << 1); + else if (strcmp(cfg->function, "dsi_b") == 0) + reg |= (1 << 1); + bus_write_4(sc->mipi_mem_res, 0, reg); /* register 0x820 */ + } + + /* Handle pin muxes */ + mux = pinmux_search_mux(pin_name); + if (mux != NULL) { + if (mux->gpio_num != -1) { + /* XXXX TODO: Reserve gpio here */ + } + rv = pinmux_config_mux(sc, pin_name, mux, cfg); + return (rv); + } + + /* Handle pin groups */ + grp = pinmux_search_grp(pin_name); + if (grp != NULL) { + rv = pinmux_config_grp(sc, pin_name, grp, cfg); + return (rv); + } + + device_printf(sc->dev, "Unknown pin: %s\n", pin_name); + return (ENXIO); +} + +static int +pinmux_read_node(struct pinmux_softc *sc, phandle_t node, struct pincfg *cfg, + char **pins, int *lpins) +{ + int rv, i; + + *lpins = OF_getprop_alloc(node, "nvidia,pins", 1, (void **)pins); + if (*lpins <= 0) + return (ENOENT); + + /* Read function (mux) settings. */ + rv = OF_getprop_alloc(node, "nvidia,function", 1, + (void **)&cfg->function); + if (rv <= 0) + cfg->function = NULL; + + /* Read numeric properties. */ + for (i = 0; i < PROP_ID_MAX_ID; i++) { + rv = OF_getencprop(node, prop_names[i].name, &cfg->params[i], + sizeof(cfg->params[i])); + if (rv <= 0) + cfg->params[i] = -1; + } + return (0); +} + +static int +pinmux_process_node(struct pinmux_softc *sc, phandle_t node) +{ + struct pincfg cfg; + char *pins, *pname; + int i, len, lpins, rv; + + rv = pinmux_read_node(sc, node, &cfg, &pins, &lpins); + if (rv != 0) + return (rv); + + len = 0; + pname = pins; + do { + i = strlen(pname) + 1; + rv = pinmux_config_node(sc, pname, &cfg); + if (rv != 0) + device_printf(sc->dev, + "Cannot configure pin: %s: %d\n", pname, rv); + + len += i; + pname += i; + } while (len < lpins); + + if (pins != NULL) + free(pins, M_OFWPROP); + if (cfg.function != NULL) + free(cfg.function, M_OFWPROP); + return (rv); +} + +static int pinmux_configure(device_t dev, phandle_t cfgxref) +{ + struct pinmux_softc *sc; + phandle_t node, cfgnode; + int rv; + + sc = device_get_softc(dev); + cfgnode = OF_node_from_xref(cfgxref); + + + for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { + if (!fdt_is_enabled(node)) + continue; + rv = pinmux_process_node(sc, node); + } + return (0); +} + +static int +pinmux_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "Tegra pin configuration"); + return (BUS_PROBE_DEFAULT); +} + +static int +pinmux_detach(device_t dev) +{ + + /* This device is always present. */ + return (EBUSY); +} + +static int +pinmux_attach(device_t dev) +{ + struct pinmux_softc * sc; + int rid; + + sc = device_get_softc(dev); + sc->dev = dev; + + rid = 0; + sc->pad_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->pad_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rid = 1; + sc->mux_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mux_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + rid = 2; + sc->mipi_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mipi_mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + return (ENXIO); + } + + /* Register as a pinctrl device and process default configuration */ + fdt_pinctrl_register(dev, NULL); + fdt_pinctrl_configure_by_name(dev, "boot"); + + return (0); +} + + +static device_method_t tegra_pinmux_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pinmux_probe), + DEVMETHOD(device_attach, pinmux_attach), + DEVMETHOD(device_detach, pinmux_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure,pinmux_configure), + + DEVMETHOD_END +}; + +static driver_t tegra_pinmux_driver = { + "tegra_pinmux", + tegra_pinmux_methods, + sizeof(struct pinmux_softc), +}; + +static devclass_t tegra_pinmux_devclass; + +EARLY_DRIVER_MODULE(tegra_pinmux, simplebus, tegra_pinmux_driver, + tegra_pinmux_devclass, 0, 0, 71); |