[RFC PATCH 00/10] arm: add support for SoC S5P4418

This patch adds support for SAMSUNG's/NEXELL's ARM Cortex-A9 based S5P4418 SoC, especially FriendlyARM's NanoPi2 and NanoPC-T2 boards. It is based on the following FriendlyARM's U-Boot version: https://github.com/friendlyarm/u-boot/tree/nanopi2-v2016.01.
The added mach-nexell also supports the S5P6818 SoC which is a follow-up of the S5P4418.
Main changes in relation to nanopi2-v2016.01: - Cosmetic changes due to patman warnings/errors. - MMC and Video drivers changed to DM. - Configs reworked (e.g. "CONFIG_..." moved from s5p4418_nanopi2.h to s5p4418_nanopi2_defconfig) - SPL related files are not included. - MACH_TYPE_S5P4418 is not defined/used anymore. - arch/arm/mach-nexell/include/mach/boot0.h added to generate the NSIH (Nexell System Information Header), substitudes tools/nexell. - board/s5p4418/ renamed to board/friendlyarm/ - Only the NanoPi2 and NanoPC-T2 boards are supported yet because I do only have the NanoPC-T2 board to test the code (this board uses the NanoPi2 code).
Stefan Bosch (10): arm: add mach-nexell (header files) arm: add mach-nexell (all files except header files) i2c: mmc: add nexell driver (gpio, i2c, mmc, pwm) video: add nexell video driver (soc: displaytop) video: add nexell video driver (soc: mlc, mipi) video: add nexell video driver (soc: lvds, hdmi) video: add nexell video driver (soc: dpc, makefile) video: add nexell video driver (display/video driver) arm: add support for SoC s5p4418 (cpu) / nanopi2 board arm: add (default) config for nanopi2 board
MAINTAINERS | 19 + arch/arm/Kconfig | 7 + arch/arm/Makefile | 1 + arch/arm/cpu/armv7/Makefile | 1 + arch/arm/cpu/armv7/s5p4418/Makefile | 6 + arch/arm/cpu/armv7/s5p4418/cpu.c | 120 ++ arch/arm/dts/Makefile | 3 + arch/arm/dts/s5p4418-nanopi2.dts | 109 ++ arch/arm/dts/s5p4418-pinctrl.dtsi | 66 + arch/arm/dts/s5p4418.dtsi | 157 ++ arch/arm/mach-nexell/Kconfig | 67 + arch/arm/mach-nexell/Makefile | 15 + arch/arm/mach-nexell/clock.c | 869 +++++++++ arch/arm/mach-nexell/cmd_boot_linux.c | 145 ++ arch/arm/mach-nexell/config.mk | 11 + arch/arm/mach-nexell/include/mach/boot0.h | 40 + arch/arm/mach-nexell/include/mach/clk.h | 24 + arch/arm/mach-nexell/include/mach/display.h | 273 +++ arch/arm/mach-nexell/include/mach/display_dev.h | 37 + arch/arm/mach-nexell/include/mach/ehci.h | 106 ++ arch/arm/mach-nexell/include/mach/gpio.h | 17 + arch/arm/mach-nexell/include/mach/mipi_display.h | 219 +++ arch/arm/mach-nexell/include/mach/nexell.h | 352 ++++ arch/arm/mach-nexell/include/mach/nx_gpio.h | 103 ++ arch/arm/mach-nexell/include/mach/reset.h | 19 + arch/arm/mach-nexell/include/mach/sec_reg.h | 15 + arch/arm/mach-nexell/include/mach/tieoff.h | 423 +++++ arch/arm/mach-nexell/nx_gpio.c | 352 ++++ arch/arm/mach-nexell/nx_sec_reg.c | 82 + arch/arm/mach-nexell/reg-call.S | 23 + arch/arm/mach-nexell/reset.c | 33 + arch/arm/mach-nexell/serial.c | 262 +++ arch/arm/mach-nexell/tieoff.c | 109 ++ arch/arm/mach-nexell/timer.c | 297 ++++ board/friendlyarm/Kconfig | 39 + board/friendlyarm/nanopi2/Kconfig | 12 + board/friendlyarm/nanopi2/MAINTAINERS | 7 + board/friendlyarm/nanopi2/Makefile | 6 + board/friendlyarm/nanopi2/board.c | 581 ++++++ board/friendlyarm/nanopi2/hwrev.c | 122 ++ board/friendlyarm/nanopi2/hwrev.h | 29 + board/friendlyarm/nanopi2/lcds.c | 703 ++++++++ board/friendlyarm/nanopi2/nxp-fb.h | 99 ++ board/friendlyarm/nanopi2/onewire.c | 323 ++++ board/friendlyarm/nanopi2/onewire.h | 29 + configs/s5p4418_nanopi2_defconfig | 177 ++ doc/README.s5p4418 | 63 + drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/nx_gpio.c | 252 +++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/nx_i2c.c | 537 ++++++ drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/nexell_dw_mmc_dm.c | 350 ++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-nexell.c | 252 +++ drivers/pwm/pwm-nexell.h | 54 + drivers/video/Kconfig | 10 + drivers/video/Makefile | 1 + drivers/video/nexell/Kconfig | 27 + drivers/video/nexell/Makefile | 12 + drivers/video/nexell/s5pxx18_dp.c | 338 ++++ drivers/video/nexell/s5pxx18_dp_hdmi.c | 543 ++++++ drivers/video/nexell/s5pxx18_dp_lvds.c | 274 +++ drivers/video/nexell/s5pxx18_dp_mipi.c | 677 +++++++ drivers/video/nexell/s5pxx18_dp_rgb.c | 69 + drivers/video/nexell/soc/Makefile | 11 + drivers/video/nexell/soc/s5pxx18_soc_disptop.c | 185 ++ drivers/video/nexell/soc/s5pxx18_soc_disptop.h | 385 ++++ drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.c | 309 ++++ drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.h | 59 + drivers/video/nexell/soc/s5pxx18_soc_disptype.h | 23 + drivers/video/nexell/soc/s5pxx18_soc_dpc.c | 1569 +++++++++++++++++ drivers/video/nexell/soc/s5pxx18_soc_dpc.h | 444 +++++ drivers/video/nexell/soc/s5pxx18_soc_hdmi.c | 50 + drivers/video/nexell/soc/s5pxx18_soc_hdmi.h | 488 +++++ drivers/video/nexell/soc/s5pxx18_soc_lvds.c | 278 +++ drivers/video/nexell/soc/s5pxx18_soc_lvds.h | 83 + drivers/video/nexell/soc/s5pxx18_soc_mipi.c | 580 ++++++ drivers/video/nexell/soc/s5pxx18_soc_mipi.h | 291 +++ drivers/video/nexell/soc/s5pxx18_soc_mlc.c | 1861 ++++++++++++++++++++ drivers/video/nexell/soc/s5pxx18_soc_mlc.h | 429 +++++ drivers/video/nexell_display.c | 658 +++++++ include/configs/s5p4418_nanopi2.h | 270 +++ 86 files changed, 17969 insertions(+) create mode 100644 arch/arm/cpu/armv7/s5p4418/Makefile create mode 100644 arch/arm/cpu/armv7/s5p4418/cpu.c create mode 100644 arch/arm/dts/s5p4418-nanopi2.dts create mode 100644 arch/arm/dts/s5p4418-pinctrl.dtsi create mode 100644 arch/arm/dts/s5p4418.dtsi create mode 100644 arch/arm/mach-nexell/Kconfig create mode 100644 arch/arm/mach-nexell/Makefile create mode 100644 arch/arm/mach-nexell/clock.c create mode 100644 arch/arm/mach-nexell/cmd_boot_linux.c create mode 100644 arch/arm/mach-nexell/config.mk create mode 100644 arch/arm/mach-nexell/include/mach/boot0.h create mode 100644 arch/arm/mach-nexell/include/mach/clk.h create mode 100644 arch/arm/mach-nexell/include/mach/display.h create mode 100644 arch/arm/mach-nexell/include/mach/display_dev.h create mode 100644 arch/arm/mach-nexell/include/mach/ehci.h create mode 100644 arch/arm/mach-nexell/include/mach/gpio.h create mode 100644 arch/arm/mach-nexell/include/mach/mipi_display.h create mode 100644 arch/arm/mach-nexell/include/mach/nexell.h create mode 100644 arch/arm/mach-nexell/include/mach/nx_gpio.h create mode 100644 arch/arm/mach-nexell/include/mach/reset.h create mode 100644 arch/arm/mach-nexell/include/mach/sec_reg.h create mode 100644 arch/arm/mach-nexell/include/mach/tieoff.h create mode 100644 arch/arm/mach-nexell/nx_gpio.c create mode 100644 arch/arm/mach-nexell/nx_sec_reg.c create mode 100644 arch/arm/mach-nexell/reg-call.S create mode 100644 arch/arm/mach-nexell/reset.c create mode 100644 arch/arm/mach-nexell/serial.c create mode 100644 arch/arm/mach-nexell/tieoff.c create mode 100644 arch/arm/mach-nexell/timer.c create mode 100644 board/friendlyarm/Kconfig create mode 100644 board/friendlyarm/nanopi2/Kconfig create mode 100644 board/friendlyarm/nanopi2/MAINTAINERS create mode 100644 board/friendlyarm/nanopi2/Makefile create mode 100644 board/friendlyarm/nanopi2/board.c create mode 100644 board/friendlyarm/nanopi2/hwrev.c create mode 100644 board/friendlyarm/nanopi2/hwrev.h create mode 100644 board/friendlyarm/nanopi2/lcds.c create mode 100644 board/friendlyarm/nanopi2/nxp-fb.h create mode 100644 board/friendlyarm/nanopi2/onewire.c create mode 100644 board/friendlyarm/nanopi2/onewire.h create mode 100644 configs/s5p4418_nanopi2_defconfig create mode 100644 doc/README.s5p4418 create mode 100644 drivers/gpio/nx_gpio.c create mode 100644 drivers/i2c/nx_i2c.c create mode 100644 drivers/mmc/nexell_dw_mmc_dm.c create mode 100644 drivers/pwm/pwm-nexell.c create mode 100644 drivers/pwm/pwm-nexell.h create mode 100644 drivers/video/nexell/Kconfig create mode 100644 drivers/video/nexell/Makefile create mode 100644 drivers/video/nexell/s5pxx18_dp.c create mode 100644 drivers/video/nexell/s5pxx18_dp_hdmi.c create mode 100644 drivers/video/nexell/s5pxx18_dp_lvds.c create mode 100644 drivers/video/nexell/s5pxx18_dp_mipi.c create mode 100644 drivers/video/nexell/s5pxx18_dp_rgb.c create mode 100644 drivers/video/nexell/soc/Makefile create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptop.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptop.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptype.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_dpc.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_dpc.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_hdmi.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_hdmi.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_lvds.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_lvds.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_mipi.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_mipi.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_mlc.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_mlc.h create mode 100644 drivers/video/nexell_display.c create mode 100644 include/configs/s5p4418_nanopi2.h

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01: - DM_VIDEO support (display_dev.h). - boot0.h added, handles NSIH --> tools/nexell obsolete. - gpio.h: Include-path to errno.h changed.
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
arch/arm/mach-nexell/include/mach/boot0.h | 40 +++ arch/arm/mach-nexell/include/mach/clk.h | 24 ++ arch/arm/mach-nexell/include/mach/display.h | 273 +++++++++++++++ arch/arm/mach-nexell/include/mach/display_dev.h | 37 ++ arch/arm/mach-nexell/include/mach/ehci.h | 106 ++++++ arch/arm/mach-nexell/include/mach/gpio.h | 17 + arch/arm/mach-nexell/include/mach/mipi_display.h | 219 ++++++++++++ arch/arm/mach-nexell/include/mach/nexell.h | 352 +++++++++++++++++++ arch/arm/mach-nexell/include/mach/nx_gpio.h | 103 ++++++ arch/arm/mach-nexell/include/mach/reset.h | 19 + arch/arm/mach-nexell/include/mach/sec_reg.h | 15 + arch/arm/mach-nexell/include/mach/tieoff.h | 423 +++++++++++++++++++++++ 12 files changed, 1628 insertions(+) create mode 100644 arch/arm/mach-nexell/include/mach/boot0.h create mode 100644 arch/arm/mach-nexell/include/mach/clk.h create mode 100644 arch/arm/mach-nexell/include/mach/display.h create mode 100644 arch/arm/mach-nexell/include/mach/display_dev.h create mode 100644 arch/arm/mach-nexell/include/mach/ehci.h create mode 100644 arch/arm/mach-nexell/include/mach/gpio.h create mode 100644 arch/arm/mach-nexell/include/mach/mipi_display.h create mode 100644 arch/arm/mach-nexell/include/mach/nexell.h create mode 100644 arch/arm/mach-nexell/include/mach/nx_gpio.h create mode 100644 arch/arm/mach-nexell/include/mach/reset.h create mode 100644 arch/arm/mach-nexell/include/mach/sec_reg.h create mode 100644 arch/arm/mach-nexell/include/mach/tieoff.h
diff --git a/arch/arm/mach-nexell/include/mach/boot0.h b/arch/arm/mach-nexell/include/mach/boot0.h new file mode 100644 index 0000000..e05c07e --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/boot0.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * NSIH (Nexell System Information Header) for FriendlyArm nanopi2 board + * + * The NSIH (first 512 Bytes of u-boot.bin) is necessary for the + * 2nd-Bootloader to get information like load address of U-Boot. + * + * 0x400 must be added to CONFIG_SYS_TEXT_BASE to have the actual load and + * start address because 2nd-Bootloader loads with an offset of 0x400 + * (NSIH + 0x200 bytes are not loaded into RAM). + * + * It has been tested / is working with the following 2nd-Bootloader: + * "BL1 by Nexell V1.0.0-gd551e13 [Built on 2018-01-25 16:58:29]" + * + * (C) Copyright 2020 Stefan Bosch stefan_b@posteo.net + */ + +#ifndef __BOOT0_H +#define __BOOT0_H + + ARM_VECTORS + .space 0x30 + .word (_end - _start) + 20 * 1024 /* 0x50: load size + * (bin + 20k for DTB) */ + .space 0x4 + .word CONFIG_SYS_TEXT_BASE + 0x400 /* 0x58: load address */ + .word 0x00000000 + .word CONFIG_SYS_TEXT_BASE + 0x400 /* 0x60: start address */ + .space 0x198 + .byte 'N' /* 0x1FC: "NSIH" signature */ + .byte 'S' + .byte 'I' + .byte 'H' + + /* The NSIH + 0x200 bytes are omitted by the 2nd-Bootloader */ + .space 0x200 +_start: + ARM_VECTORS + +#endif /* __BOOT0_H */ diff --git a/arch/arm/mach-nexell/include/mach/clk.h b/arch/arm/mach-nexell/include/mach/clk.h new file mode 100644 index 0000000..cc5589a --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/clk.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2016 Nexell + * Hyunseok, Jung hsjung@nexell.co.kr + */ + +#ifndef __ASM_ARM_ARCH_CLK_H_ +#define __ASM_ARM_ARCH_CLK_H_ + +struct clk { + unsigned long rate; +}; + +void clk_init(void); + +struct clk *clk_get(const char *id); +void clk_put(struct clk *clk); +unsigned long clk_get_rate(struct clk *clk); +long clk_round_rate(struct clk *clk, unsigned long rate); +int clk_set_rate(struct clk *clk, unsigned long rate); +int clk_enable(struct clk *clk); +void clk_disable(struct clk *clk); + +#endif diff --git a/arch/arm/mach-nexell/include/mach/display.h b/arch/arm/mach-nexell/include/mach/display.h new file mode 100644 index 0000000..b167e63 --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/display.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _NX__DISPLAY_H_ +#define _NX__DISPLAY_H_ + +#define DP_PLANS_NUM 3 + +/* the display output format. */ +#define DPC_FORMAT_RGB555 0 /* RGB555 Format */ +#define DPC_FORMAT_RGB565 1 /* RGB565 Format */ +#define DPC_FORMAT_RGB666 2 /* RGB666 Format */ +#define DPC_FORMAT_RGB888 3 /* RGB888 Format */ +#define DPC_FORMAT_MRGB555A 4 /* MRGB555A Format */ +#define DPC_FORMAT_MRGB555B 5 /* MRGB555B Format */ +#define DPC_FORMAT_MRGB565 6 /* MRGB565 Format */ +#define DPC_FORMAT_MRGB666 7 /* MRGB666 Format */ +#define DPC_FORMAT_MRGB888A 8 /* MRGB888A Format */ +#define DPC_FORMAT_MRGB888B 9 /* MRGB888B Format */ +#define DPC_FORMAT_CCIR656 10 /* ITU-R BT.656 / 601(8-bit) */ +#define DPC_FORMAT_CCIR601A 12 /* ITU-R BT.601A */ +#define DPC_FORMAT_CCIR601B 13 /* ITU-R BT.601B */ +#define DPC_FORMAT_4096COLOR 1 /* 4096 Color Format */ +#define DPC_FORMAT_16GRAY 3 /* 16 Level Gray Format */ + +/* layer pixel format. */ +#define MLC_RGBFMT_R5G6B5 0x44320000 /* {R5,G6,B5 }. */ +#define MLC_RGBFMT_B5G6R5 0xC4320000 /* {B5,G6,R5 }. */ +#define MLC_RGBFMT_X1R5G5B5 0x43420000 /* {X1,R5,G5,B5}. */ +#define MLC_RGBFMT_X1B5G5R5 0xC3420000 /* {X1,B5,G5,R5}. */ +#define MLC_RGBFMT_X4R4G4B4 0x42110000 /* {X4,R4,G4,B4}. */ +#define MLC_RGBFMT_X4B4G4R4 0xC2110000 /* {X4,B4,G4,R4}. */ +#define MLC_RGBFMT_X8R3G3B2 0x41200000 /* {X8,R3,G3,B2}. */ +#define MLC_RGBFMT_X8B3G3R2 0xC1200000 /* {X8,B3,G3,R2}. */ +#define MLC_RGBFMT_A1R5G5B5 0x33420000 /* {A1,R5,G5,B5}. */ +#define MLC_RGBFMT_A1B5G5R5 0xB3420000 /* {A1,B5,G5,R5}. */ +#define MLC_RGBFMT_A4R4G4B4 0x22110000 /* {A4,R4,G4,B4}. */ +#define MLC_RGBFMT_A4B4G4R4 0xA2110000 /* {A4,B4,G4,R4}. */ +#define MLC_RGBFMT_A8R3G3B2 0x11200000 /* {A8,R3,G3,B2}. */ +#define MLC_RGBFMT_A8B3G3R2 0x91200000 /* {A8,B3,G3,R2}. */ +#define MLC_RGBFMT_R8G8B8 0x46530000 /* {R8,G8,B8 }. */ +#define MLC_RGBFMT_B8G8R8 0xC6530000 /* {B8,G8,R8 }. */ +#define MLC_RGBFMT_X8R8G8B8 0x46530000 /* {X8,R8,G8,B8}. */ +#define MLC_RGBFMT_X8B8G8R8 0xC6530000 /* {X8,B8,G8,R8}. */ +#define MLC_RGBFMT_A8R8G8B8 0x06530000 /* {A8,R8,G8,B8}. */ +#define MLC_RGBFMT_A8B8G8R8 0x86530000 /* {A8,B8,G8,R8}. */ + +/* the data output order in case of ITU-R BT.656 / 601. */ +#define DPC_YCORDER_CBYCRY 0 +#define DPC_YCORDER_CRYCBY 1 +#define DPC_YCORDER_YCBYCR 2 +#define DPC_YCORDER_YCRYCB 3 + +/* the PAD output clock. */ +#define DPC_PADCLKSEL_VCLK 0 /* VCLK */ +#define DPC_PADCLKSEL_VCLK2 1 /* VCLK2 */ + +/* display sync info for DPC */ +struct dp_sync_info { + int interlace; + int h_active_len; + int h_sync_width; + int h_back_porch; + int h_front_porch; + int h_sync_invert; /* default active low */ + int v_active_len; + int v_sync_width; + int v_back_porch; + int v_front_porch; + int v_sync_invert; /* default active low */ + int pixel_clock_hz; /* HZ */ +}; + +/* syncgen control (DPC) */ +#define DP_SYNC_DELAY_RGB_PVD (1 << 0) +#define DP_SYNC_DELAY_HSYNC_CP1 (1 << 1) +#define DP_SYNC_DELAY_VSYNC_FRAM (1 << 2) +#define DP_SYNC_DELAY_DE_CP (1 << 3) + +struct dp_ctrl_info { + /* clock gen */ + int clk_src_lv0; + int clk_div_lv0; + int clk_src_lv1; + int clk_div_lv1; + /* scan format */ + int interlace; + /* syncgen format */ + unsigned int out_format; + int invert_field; /* 0:normal(Low odd), 1:invert (low even) */ + int swap_RB; + unsigned int yc_order; /* for CCIR output */ + /* extern sync delay */ + int delay_mask; /* if 0, set defalut delays */ + int d_rgb_pvd; /* delay for RGB/PVD, 0~16, default 0 */ + int d_hsync_cp1; /* delay for HSYNC/CP1, 0~63, default 12 */ + int d_vsync_fram; /* delay for VSYNC/FRAM, 0~63, default 12 */ + int d_de_cp2; /* delay for DE/CP2, 0~63, default 12 */ + /* sync offset */ + int vs_start_offset; /* start vsync offset, defatult 0 */ + int vs_end_offset; /* end vsync offset, default 0 */ + int ev_start_offset; /* start even vsync offset, default 0 */ + int ev_end_offset; /* end even vsync offset , default 0 */ + /* pad clock seletor */ + int vck_select; /* 0=vclk0, 1=vclk2 */ + int clk_inv_lv0; /* OUTCLKINVn */ + int clk_delay_lv0; /* OUTCLKDELAYn */ + int clk_inv_lv1; /* OUTCLKINVn */ + int clk_delay_lv1; /* OUTCLKDELAYn */ + int clk_sel_div1; /* 0=clk1_inv, 1=clk1_div_2_ns */ +}; + +/* multi layer control (MLC) */ +struct dp_plane_top { + int screen_width; + int screen_height; + int video_prior; /* 0: video>RGBn, 1: RGB0>video>RGB1, + * 2: RGB0 > RGB1 > video .. */ + int interlace; + int plane_num; + unsigned int back_color; +}; + +struct dp_plane_info { + int layer; + unsigned int fb_base; + int left; + int top; + int width; + int height; + int pixel_byte; + unsigned int format; + int alpha_on; + int alpha_depth; + int tp_on; /* transparency color enable */ + unsigned int tp_color; + unsigned int mem_lock_size; /* memory burst access (4,8,16) */ + int video_layer; + int enable; +}; + +/* + * LCD device dependency struct + * RGB, LVDS, MiPi, HDMI + */ +enum { + DP_DEVICE_RESCONV = 0, + DP_DEVICE_RGBLCD = 1, + DP_DEVICE_HDMI = 2, + DP_DEVICE_MIPI = 3, + DP_DEVICE_LVDS = 4, + DP_DEVICE_CVBS = 5, + DP_DEVICE_DP0 = 6, + DP_DEVICE_DP1 = 7, + DP_DEVICE_END, +}; + +enum { + DP_CLOCK_RESCONV = 0, + DP_CLOCK_LCDIF = 1, + DP_CLOCK_MIPI = 2, + DP_CLOCK_LVDS = 3, + DP_CLOCK_HDMI = 4, + DP_CLOCK_END, +}; + +enum dp_lvds_format { + DP_LVDS_FORMAT_VESA = 0, + DP_LVDS_FORMAT_JEIDA = 1, + DP_LVDS_FORMAT_LOC = 2, +}; + +#define DEF_VOLTAGE_LEVEL (0x20) + +struct dp_lvds_dev { + enum dp_lvds_format lvds_format; /* 0:VESA, 1:JEIDA, 2: Location */ + int pol_inv_hs; /* hsync polarity invert for VESA, JEIDA */ + int pol_inv_vs; /* bsync polarity invert for VESA, JEIDA */ + int pol_inv_de; /* de polarity invert for VESA, JEIDA */ + int pol_inv_ck; /* input clock(pixel clock) polarity invert */ + int voltage_level; + /* Location setting */ + unsigned int loc_map[9]; /* Location Setting */ + unsigned int loc_mask[2]; /* Location Setting, 0 ~ 34 */ + unsigned int loc_pol[2]; /* Location Setting, 0 ~ 34 */ +}; + +#include "mipi_display.h" + +struct dp_mipi_dev { + int lp_bitrate; /* to lcd setup, low power bitrate (150, 100, 80 Mhz) */ + int hs_bitrate; /* to lcd data, high speed bitrate (1000, ... Mhz) */ + int lpm_trans; + int command_mode; + unsigned int hs_pllpms; + unsigned int hs_bandctl; + unsigned int lp_pllpms; + unsigned int lp_bandctl; + struct mipi_dsi_device dsi; +}; + +struct dp_rgb_dev { + int lcd_mpu_type; +}; + +struct dp_hdmi_dev { + int preset; +}; + +/* platform data for the driver model */ +struct nx_display_platdata { + int module; + struct dp_sync_info sync; + struct dp_ctrl_info ctrl; + struct dp_plane_top top; + struct dp_plane_info plane[DP_PLANS_NUM]; + int dev_type; + void *device; +}; + +/* Lcd api */ +void nx_lvds_display(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_plane_top *top, + struct dp_plane_info *planes, + struct dp_lvds_dev *dev); + +void nx_rgb_display(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_plane_top *top, struct dp_plane_info *planes, + struct dp_rgb_dev *dev); + +void nx_hdmi_display(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_plane_top *top, + struct dp_plane_info *planes, + struct dp_hdmi_dev *dev); + +void nx_mipi_display(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_plane_top *top, + struct dp_plane_info *planes, + struct dp_mipi_dev *dev); + +int nx_mipi_dsi_lcd_bind(struct mipi_dsi_device *dsi); + +/* disaply api */ +void dp_control_init(int module); +int dp_control_setup(int module, struct dp_sync_info *sync, + struct dp_ctrl_info *ctrl); +void dp_control_enable(int module, int on); + +void dp_plane_init(int module); +int dp_plane_screen_setup(int module, struct dp_plane_top *top); +void dp_plane_screen_enable(int module, int on); + +int dp_plane_layer_setup(int module, struct dp_plane_info *plane); +void dp_plane_layer_enable(int module, struct dp_plane_info *plane, int on); + +int dp_plane_set_enable(int module, int layer, int on); +int dp_plane_set_address(int module, int layer, unsigned int address); +int dp_plane_wait_vsync(int module, int layer, int fps); + +#if defined CONFIG_SPL_BUILD || \ + (!defined(CONFIG_DM) && !defined(CONFIG_OF_CONTROL)) +int nx_display_probe(struct nx_display_platdata *plat); +#endif + +#endif diff --git a/arch/arm/mach-nexell/include/mach/display_dev.h b/arch/arm/mach-nexell/include/mach/display_dev.h new file mode 100644 index 0000000..77eb614 --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/display_dev.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _NX__DISPLAY_DEV_H_ +#define _NX__DISPLAY_DEV_H_ + +#if defined CONFIG_VIDEO || defined CONFIG_DM_VIDEO +#include <video_fb.h> +#elif defined CONFIG_LCD +#include <lcd.h> +#endif + +struct nx_display_dev { +#if defined CONFIG_VIDEO || defined CONFIG_DM_VIDEO + GraphicDevice graphic_device; +#elif defined CONFIG_LCD + vidinfo_t *panel_info; +#endif + unsigned long base; + int module; + struct dp_sync_info sync; + struct dp_ctrl_info ctrl; + struct dp_plane_top top; + struct dp_plane_info planes[DP_PLANS_NUM]; + int dev_type; + void *device; + struct dp_plane_info *fb_plane; + unsigned int depth; /* byte per pixel */ + unsigned int fb_addr; + unsigned int fb_size; +}; + +#endif diff --git a/arch/arm/mach-nexell/include/mach/ehci.h b/arch/arm/mach-nexell/include/mach/ehci.h new file mode 100644 index 0000000..545153b --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/ehci.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * NEXELL USB HOST EHCI Controller + * + * (C) Copyright 2016 Nexell + * Hyunseok, Jung hsjung@nexell.co.kr + */ + +#ifndef __ASM_ARM_ARCH_EHCI_H__ +#define __ASM_ARM_ARCH_EHCI_H__ + +/* Nexell USBHOST PHY registers */ + +/* USBHOST Configuration 0 Register */ +#define NX_HOST_CON0 0x14 +#define NX_HOST_CON0_SS_WORD_IF BIT(26) +#define NX_HOST_CON0_SS_WORD_IF_ENB BIT(25) +#define NX_HOST_CON0_SS_WORD_IF_16 ( \ + NX_HOST_CON0_SS_WORD_IF | \ + NX_HOST_CON0_SS_WORD_IF_ENB) + +#define NX_HOST_CON0_HSIC_480M_FROM_OTG_PHY BIT(24) +#define NX_HOST_CON0_HSIC_FREE_CLOCK_ENB BIT(23) +#define NX_HOST_CON0_HSIC_CLK_MASK (0x3 << 23) + +#define NX_HOST_CON0_N_HOST_HSIC_RESET_SYNC BIT(22) +#define NX_HOST_CON0_N_HOST_UTMI_RESET_SYNC BIT(21) +#define NX_HOST_CON0_N_HOST_PHY_RESET_SYNC BIT(20) +#define NX_HOST_CON0_UTMI_RESET_SYNC ( \ + NX_HOST_CON0_N_HOST_HSIC_RESET_SYNC | \ + NX_HOST_CON0_N_HOST_UTMI_RESET_SYNC | \ + NX_HOST_CON0_N_HOST_PHY_RESET_SYNC) + +#define NX_HOST_CON0_N_AUXWELL_RESET_SYNC BIT(19) +#define NX_HOST_CON0_N_OHCI_RESET_SYNC BIT(18) +#define NX_HOST_CON0_N_RESET_SYNC BIT(17) +#define NX_HOST_CON0_AHB_RESET_SYNC ( \ + NX_HOST_CON0_N_AUXWELL_RESET_SYNC | \ + NX_HOST_CON0_N_OHCI_RESET_SYNC | \ + NX_HOST_CON0_N_RESET_SYNC) + +#define NX_HOST_CON0_HSIC_EN_PORT1 (0x2 << 14) +#define NX_HOST_CON0_HSIC_EN_MASK (0x7 << 14) + +/* USBHOST Configuration 1 Register */ +#define NX_HOST_CON1 0x18 + +/* USBHOST Configuration 2 Register */ +#define NX_HOST_CON2 0x1C +#define NX_HOST_CON2_SS_ENA_INCRX_ALIGN (0x1 << 28) +#define NX_HOST_CON2_SS_ENA_INCR4 (0x1 << 27) +#define NX_HOST_CON2_SS_ENA_INCR8 (0x1 << 26) +#define NX_HOST_CON2_SS_ENA_INCR16 (0x1 << 25) +#define NX_HOST_CON2_SS_DMA_BURST_MASK \ + (NX_HOST_CON2_SS_ENA_INCR16 | NX_HOST_CON2_SS_ENA_INCR8 | \ + NX_HOST_CON2_SS_ENA_INCR4 | NX_HOST_CON2_SS_ENA_INCRX_ALIGN) + +#define NX_HOST_CON2_EHCI_SS_ENABLE_DMA_BURST \ + (NX_HOST_CON2_SS_ENA_INCR16 | NX_HOST_CON2_SS_ENA_INCR8 | \ + NX_HOST_CON2_SS_ENA_INCR4 | NX_HOST_CON2_SS_ENA_INCRX_ALIGN) + +#define NX_HOST_CON2_OHCI_SS_ENABLE_DMA_BURST \ + (NX_HOST_CON2_SS_ENA_INCR4 | NX_HOST_CON2_SS_ENA_INCRX_ALIGN) + +#define NX_HOST_CON2_SS_FLADJ_VAL_0_OFFSET (21) +#define NX_HOST_CON2_SS_FLADJ_VAL_OFFSET (3) +#define NX_HOST_CON2_SS_FLADJ_VAL_NUM (6) +#define NX_HOST_CON2_SS_FLADJ_VAL_0_SEL BIT(5) +#define NX_HOST_CON2_SS_FLADJ_VAL_MAX 0x7 + +/* USBHOST Configuration 3 Register */ +#define NX_HOST_CON3 0x20 +#define NX_HOST_CON3_POR BIT(8) +#define NX_HOST_CON3_POR_ENB BIT(7) +#define NX_HOST_CON3_POR_MASK (0x3 << 7) + +/* USBHOST Configuration 4 Register */ +#define NX_HOST_CON4 0x24 +#define NX_HOST_CON4_WORDINTERFACE BIT(9) +#define NX_HOST_CON4_WORDINTERFACE_ENB BIT(8) +#define NX_HOST_CON4_WORDINTERFACE_16 ( \ + NX_HOST_CON4_WORDINTERFACE | \ + NX_HOST_CON4_WORDINTERFACE_ENB) + +/* USBHOST Configuration 5 Register */ +#define NX_HOST_CON5 0x28 +#define NX_HOST_CON5_HSIC_POR BIT(19) +#define NX_HOST_CON5_HSIC_POR_ENB BIT(18) +#define NX_HOST_CON5_HSIC_POR_MASK (0x3 << 18) + +/* USBHOST Configuration 6 Register */ +#define NX_HOST_CON6 0x2C +#define NX_HOST_CON6_HSIC_WORDINTERFACE BIT(13) +#define NX_HOST_CON6_HSIC_WORDINTERFACE_ENB BIT(12) +#define NX_HOST_CON6_HSIC_WORDINTERFACE_16 ( \ + NX_HOST_CON6_HSIC_WORDINTERFACE | \ + NX_HOST_CON6_HSIC_WORDINTERFACE_ENB) + +/* Register map for PHY control */ +struct nx_usb_phy { + unsigned int reserved; + unsigned int others[4]; + unsigned int usbhost_con[7]; +}; + +#endif /* __ASM_ARM_ARCH_EHCI_H__ */ diff --git a/arch/arm/mach-nexell/include/mach/gpio.h b/arch/arm/mach-nexell/include/mach/gpio.h new file mode 100644 index 0000000..7167d3c --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/gpio.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2016 Nexell + * DeokJin, Lee truevirtue@nexell.co.kr + */ + +#ifndef __ASM_ARCH_NEXELL_GPIO_H +#define __ASM_ARCH_NEXELL_GPIO_H + +#include <asm/io.h> +#include <linux/errno.h> + +#define PIN_BASE 0 + +#define MAX_GPIO_BANKS 5 + +#endif /* __ASM_ARCH_NEXELL_GPIO_H */ diff --git a/arch/arm/mach-nexell/include/mach/mipi_display.h b/arch/arm/mach-nexell/include/mach/mipi_display.h new file mode 100644 index 0000000..f10f4ae --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/mipi_display.h @@ -0,0 +1,219 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Defines for Mobile Industry Processor Interface (MIPI(R)) + * Display Working Group standards: DSI, DCS, DBI, DPI + * + * Copyright (C) 2010 Guennadi Liakhovetski g.liakhovetski@gmx.de + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak imre.deak@nokia.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MIPI_DISPLAY_H +#define MIPI_DISPLAY_H + +/* MIPI DSI Processor-to-Peripheral transaction types */ +enum { + MIPI_DSI_V_SYNC_START = 0x01, + MIPI_DSI_V_SYNC_END = 0x11, + MIPI_DSI_H_SYNC_START = 0x21, + MIPI_DSI_H_SYNC_END = 0x31, + + MIPI_DSI_COLOR_MODE_OFF = 0x02, + MIPI_DSI_COLOR_MODE_ON = 0x12, + MIPI_DSI_SHUTDOWN_PERIPHERAL = 0x22, + MIPI_DSI_TURN_ON_PERIPHERAL = 0x32, + + MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM = 0x03, + MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM = 0x13, + MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM = 0x23, + + MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM = 0x04, + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM = 0x14, + MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM = 0x24, + + MIPI_DSI_DCS_SHORT_WRITE = 0x05, + MIPI_DSI_DCS_SHORT_WRITE_PARAM = 0x15, + + MIPI_DSI_DCS_READ = 0x06, + + MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE = 0x37, + + MIPI_DSI_END_OF_TRANSMISSION = 0x08, + + MIPI_DSI_NULL_PACKET = 0x09, + MIPI_DSI_BLANKING_PACKET = 0x19, + MIPI_DSI_GENERIC_LONG_WRITE = 0x29, + MIPI_DSI_DCS_LONG_WRITE = 0x39, + + MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20 = 0x0c, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24 = 0x1c, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16 = 0x2c, + + MIPI_DSI_PACKED_PIXEL_STREAM_30 = 0x0d, + MIPI_DSI_PACKED_PIXEL_STREAM_36 = 0x1d, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12 = 0x3d, + + MIPI_DSI_PACKED_PIXEL_STREAM_16 = 0x0e, + MIPI_DSI_PACKED_PIXEL_STREAM_18 = 0x1e, + MIPI_DSI_PIXEL_STREAM_3BYTE_18 = 0x2e, + MIPI_DSI_PACKED_PIXEL_STREAM_24 = 0x3e, +}; + +/* MIPI DSI Peripheral-to-Processor transaction types */ +enum { + MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT = 0x02, + MIPI_DSI_RX_END_OF_TRANSMISSION = 0x08, + MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE = 0x11, + MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE = 0x12, + MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE = 0x1a, + MIPI_DSI_RX_DCS_LONG_READ_RESPONSE = 0x1c, + MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE = 0x21, + MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE = 0x22, +}; + +/* MIPI DCS commands */ +enum { + MIPI_DCS_NOP = 0x00, + MIPI_DCS_SOFT_RESET = 0x01, + MIPI_DCS_GET_DISPLAY_ID = 0x04, + MIPI_DCS_GET_RED_CHANNEL = 0x06, + MIPI_DCS_GET_GREEN_CHANNEL = 0x07, + MIPI_DCS_GET_BLUE_CHANNEL = 0x08, + MIPI_DCS_GET_DISPLAY_STATUS = 0x09, + MIPI_DCS_GET_POWER_MODE = 0x0A, + MIPI_DCS_GET_ADDRESS_MODE = 0x0B, + MIPI_DCS_GET_PIXEL_FORMAT = 0x0C, + MIPI_DCS_GET_DISPLAY_MODE = 0x0D, + MIPI_DCS_GET_SIGNAL_MODE = 0x0E, + MIPI_DCS_GET_DIAGNOSTIC_RESULT = 0x0F, + MIPI_DCS_ENTER_SLEEP_MODE = 0x10, + MIPI_DCS_EXIT_SLEEP_MODE = 0x11, + MIPI_DCS_ENTER_PARTIAL_MODE = 0x12, + MIPI_DCS_ENTER_NORMAL_MODE = 0x13, + MIPI_DCS_EXIT_INVERT_MODE = 0x20, + MIPI_DCS_ENTER_INVERT_MODE = 0x21, + MIPI_DCS_SET_GAMMA_CURVE = 0x26, + MIPI_DCS_SET_DISPLAY_OFF = 0x28, + MIPI_DCS_SET_DISPLAY_ON = 0x29, + MIPI_DCS_SET_COLUMN_ADDRESS = 0x2A, + MIPI_DCS_SET_PAGE_ADDRESS = 0x2B, + MIPI_DCS_WRITE_MEMORY_START = 0x2C, + MIPI_DCS_WRITE_LUT = 0x2D, + MIPI_DCS_READ_MEMORY_START = 0x2E, + MIPI_DCS_SET_PARTIAL_AREA = 0x30, + MIPI_DCS_SET_SCROLL_AREA = 0x33, + MIPI_DCS_SET_TEAR_OFF = 0x34, + MIPI_DCS_SET_TEAR_ON = 0x35, + MIPI_DCS_SET_ADDRESS_MODE = 0x36, + MIPI_DCS_SET_SCROLL_START = 0x37, + MIPI_DCS_EXIT_IDLE_MODE = 0x38, + MIPI_DCS_ENTER_IDLE_MODE = 0x39, + MIPI_DCS_SET_PIXEL_FORMAT = 0x3A, + MIPI_DCS_WRITE_MEMORY_CONTINUE = 0x3C, + MIPI_DCS_READ_MEMORY_CONTINUE = 0x3E, + MIPI_DCS_SET_TEAR_SCANLINE = 0x44, + MIPI_DCS_GET_SCANLINE = 0x45, + MIPI_DCS_READ_DDB_START = 0xA1, + MIPI_DCS_READ_DDB_CONTINUE = 0xA8, +}; + +/* MIPI DCS pixel formats */ +#define MIPI_DCS_PIXEL_FMT_24BIT 7 +#define MIPI_DCS_PIXEL_FMT_18BIT 6 +#define MIPI_DCS_PIXEL_FMT_16BIT 5 +#define MIPI_DCS_PIXEL_FMT_12BIT 3 +#define MIPI_DCS_PIXEL_FMT_8BIT 2 +#define MIPI_DCS_PIXEL_FMT_3BIT 1 + +/* request ACK from peripheral */ +#define MIPI_DSI_MSG_REQ_ACK BIT(0) +/* use Low Power Mode to transmit message */ +#define MIPI_DSI_MSG_USE_LPM BIT(1) + +/** + * struct mipi_dsi_msg - read/write DSI buffer + * @channel: virtual channel id + * @type: payload data type + * @flags: flags controlling this message transmission + * @tx_len: length of @tx_buf + * @tx_buf: data to be written + * @rx_len: length of @rx_buf + * @rx_buf: data to be read, or NULL + */ +struct mipi_dsi_msg { + u8 channel; /* virtual channel id */ + u8 type; /* payload data type */ + u16 flags; /* flags controlling this message transmission */ + size_t tx_len; + const void *tx_buf; + size_t rx_len; + void *rx_buf; +}; + +/* DSI mode flags */ + +/* video mode */ +#define MIPI_DSI_MODE_VIDEO BIT(0) +/* video burst mode */ +#define MIPI_DSI_MODE_VIDEO_BURST BIT(1) +/* video pulse mode */ +#define MIPI_DSI_MODE_VIDEO_SYNC_PULSE BIT(2) +/* enable auto vertical count mode */ +#define MIPI_DSI_MODE_VIDEO_AUTO_VERT BIT(3) +/* enable hsync-end packets in vsync-pulse and v-porch area */ +#define MIPI_DSI_MODE_VIDEO_HSE BIT(4) +/* disable hfront-porch area */ +#define MIPI_DSI_MODE_VIDEO_HFP BIT(5) +/* disable hback-porch area */ +#define MIPI_DSI_MODE_VIDEO_HBP BIT(6) +/* disable hsync-active area */ +#define MIPI_DSI_MODE_VIDEO_HSA BIT(7) +/* flush display FIFO on vsync pulse */ +#define MIPI_DSI_MODE_VSYNC_FLUSH BIT(8) +/* disable EoT packets in HS mode */ +#define MIPI_DSI_MODE_EOT_PACKET BIT(9) +/* device supports non-continuous clock behavior (DSI spec 5.6.1) */ +#define MIPI_DSI_CLOCK_NON_CONTINUOUS BIT(10) +/* transmit data in low power */ +#define MIPI_DSI_MODE_LPM BIT(11) /* DSI mode flags */ + +enum mipi_dsi_pixel_format { + MIPI_DSI_FMT_RGB888, + MIPI_DSI_FMT_RGB666, + MIPI_DSI_FMT_RGB666_PACKED, + MIPI_DSI_FMT_RGB565, +}; + +/** + * struct mipi_dsi_device - DSI peripheral device + * @host: DSI host for this peripheral + * @dev: driver model device node for this peripheral + * @channel: virtual channel assigned to the peripheral + * @format: pixel format for video mode + * @lanes: number of active data lanes + * @mode_flags: DSI operation mode related flags + */ +struct mipi_dsi_device { + unsigned int channel; + unsigned int lanes; + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; + struct mipi_panel_ops *ops; + ssize_t (*write_buffer)(struct mipi_dsi_device *dsi, + const void *data, size_t len); +}; + +struct mipi_panel_ops { + int (*init)(struct mipi_dsi_device *dsi, int width, int height); + int (*prepare)(struct mipi_dsi_device *dsi); + int (*unprepare)(struct mipi_dsi_device *dsi); + int (*enable)(struct mipi_dsi_device *dsi); + int (*disable)(struct mipi_dsi_device *dsi); + void *private_data; +}; + +#endif diff --git a/arch/arm/mach-nexell/include/mach/nexell.h b/arch/arm/mach-nexell/include/mach/nexell.h new file mode 100644 index 0000000..e42805f --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/nexell.h @@ -0,0 +1,352 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2016 Nexell + * Hyunseok, Jung hsjung@nexell.co.kr + */ + +#ifndef __NEXELL_H__ +#define __NEXELL_H__ + +#define PHY_BASEADDR_DMA0 (0xC0000000) +#define PHY_BASEADDR_DMA1 (0xC0001000) +#if defined(CONFIG_ARCH_S5P4418) +#define PHY_BASEADDR_INTC0 (0xC0002000) +#define PHY_BASEADDR_INTC1 (0xC0003000) +#elif defined(CONFIG_ARCH_S5P6818) +#define PHY_BASEADDR_INTC (0xC0008000) +#endif +#define PHY_BASEADDR_CLKPWR (0xC0010000) +#define PHY_BASEADDR_RTC (0xC0010C00) +#define PHY_BASEADDR_ALIVE (0xC0010800) +#define PHY_BASEADDR_RSTCON (0xC0012000) +#define PHY_BASEADDR_TIEOFF (0xC0011000) +#define PHY_BASEADDR_PDM (0xC0014000) +#define PHY_BASEADDR_CRYPTO (0xC0015000) +#define PHY_BASEADDR_TIMER (0xC0017000) +#define PHY_BASEADDR_PWM (0xC0018000) +#define PHY_BASEADDR_WDT (0xC0019000) +#define PHY_BASEADDR_GPIOA (0xC001A000) +#define PHY_BASEADDR_GPIOB (0xC001B000) +#define PHY_BASEADDR_GPIOC (0xC001C000) +#define PHY_BASEADDR_GPIOD (0xC001D000) +#define PHY_BASEADDR_GPIOE (0xC001E000) +#define PHY_BASEADDR_OHCI (0xC0020000) +#define PHY_BASEADDR_EHCI (0xC0030000) +#define PHY_BASEADDR_HSOTG (0xC0040000) +#define PHY_BASEADDR_ADC (0xC0053000) +#define PHY_BASEADDR_PPM (0xC0054000) +#define PHY_BASEADDR_I2S0 (0xC0055000) +#define PHY_BASEADDR_I2S1 (0xC0056000) +#define PHY_BASEADDR_I2S2 (0xC0057000) +#define PHY_BASEADDR_AC97 (0xC0058000) +#define PHY_BASEADDR_SPDIF_TX (0xC0059000) +#define PHY_BASEADDR_SPDIF_RX (0xC005A000) +#define PHY_BASEADDR_SSP0 (0xC005B000) +#define PHY_BASEADDR_SSP1 (0xC005C000) +#define PHY_BASEADDR_SSP2 (0xC005F000) +#define PHY_BASEADDR_MPEGTSI (0xC005D000) +#define PHY_BASEADDR_GMAC (0xC0060000) +#define PHY_BASEADDR_VIP0 (0xC0063000) +#define PHY_BASEADDR_VIP1 (0xC0064000) +#if defined(CONFIG_ARCH_S5P6818) +#define PHY_BASEADDR_VIP2 (0xC0099000) +#endif +#define PHY_BASEADDR_DEINTERLACE (0xC0065000) +#define PHY_BASEADDR_SCALER (0xC0066000) +#define PHY_BASEADDR_ECID (0xC0067000) +#define PHY_BASEADDR_SDMMC0 (0xC0062000) +#define PHY_BASEADDR_SDMMC1 (0xC0068000) +#define PHY_BASEADDR_SDMMC2 (0xC0069000) +#define PHY_BASEADDR_MALI400 (0xC0070000) +#define PHY_BASEADDR_CODA_APB0 (0xC0080000) +#define PHY_BASEADDR_CODA_APB1 (0xC0081000) +#define PHY_BASEADDR_CODA_APB2 (0xC0082000) +#define PHY_BASEADDR_CODA_APB3 (0xC0083000) +/* dma (O), modem(X), UART0_MODULE */ +#define PHY_BASEADDR_UART0 (0xC00A1000) +/* dma (O), modem(O), pl01115_Uart_modem_MODULE */ +#define PHY_BASEADDR_UART1 (0xC00A0000) +/* dma (O), modem(X), UART1_MODULE */ +#define PHY_BASEADDR_UART2 (0xC00A2000) +/* dma (X), modem(X), pl01115_Uart_nodma0_MODULE */ +#define PHY_BASEADDR_UART3 (0xC00A3000) +/* dma (X), modem(X), pl01115_Uart_nodma1_MODULE */ +#define PHY_BASEADDR_UART4 (0xC006D000) +/* dma (X), modem(X), pl01115_Uart_nodma2_MODULE */ +#define PHY_BASEADDR_UART5 (0xC006F000) +#define PHY_BASEADDR_I2C0 (0xC00A4000) +#define PHY_BASEADDR_I2C1 (0xC00A5000) +#define PHY_BASEADDR_I2C2 (0xC00A6000) +#define PHY_BASEADDR_CAN0 (0xC00CE000) +#define PHY_BASEADDR_CAN1 (0xC00CF000) +#define PHY_BASEADDR_MIPI (0xC00D0000) +#define PHY_BASEADDR_DISPLAYTOP (0xC0100000) + +#define PHY_BASEADDR_CLKGEN0 (0xC00BB000) /* TIMER_1 */ +#define PHY_BASEADDR_CLKGEN1 (0xC00BC000) /* TIMER_2 */ +#define PHY_BASEADDR_CLKGEN2 (0xC00BD000) /* TIMER_3 */ +#define PHY_BASEADDR_CLKGEN3 (0xC00BE000) /* PWM_1 */ +#define PHY_BASEADDR_CLKGEN4 (0xC00BF000) /* PWM_2 */ +#define PHY_BASEADDR_CLKGEN5 (0xC00C0000) /* PWM_3 */ +#define PHY_BASEADDR_CLKGEN6 (0xC00AE000) /* I2C_0 */ +#define PHY_BASEADDR_CLKGEN7 (0xC00AF000) /* I2C_1 */ +#define PHY_BASEADDR_CLKGEN8 (0xC00B0000) /* I2C_2 */ +#define PHY_BASEADDR_CLKGEN9 (0xC00CA000) /* MIPI */ +#define PHY_BASEADDR_CLKGEN10 (0xC00C8000) /* GMAC */ +#define PHY_BASEADDR_CLKGEN11 (0xC00B8000) /* SPDIF_TX */ +#define PHY_BASEADDR_CLKGEN12 (0xC00B7000) /* MPEGTSI */ +#define PHY_BASEADDR_CLKGEN13 (0xC00BA000) /* PWM_0 */ +#define PHY_BASEADDR_CLKGEN14 (0xC00B9000) /* TIMER_0 */ +#define PHY_BASEADDR_CLKGEN15 (0xC00B2000) /* I2S_0 */ +#define PHY_BASEADDR_CLKGEN16 (0xC00B3000) /* I2S_1 */ +#define PHY_BASEADDR_CLKGEN17 (0xC00B4000) /* I2S_2 */ +#define PHY_BASEADDR_CLKGEN18 (0xC00C5000) /* SDHC_0 */ +#define PHY_BASEADDR_CLKGEN19 (0xC00CC000) /* SDHC_1 */ +#define PHY_BASEADDR_CLKGEN20 (0xC00CD000) /* SDHC_2 */ +#define PHY_BASEADDR_CLKGEN21 (0xC00C3000) /* MALI */ +#define PHY_BASEADDR_CLKGEN22 (0xC00A9000) /* UART_0 */ +#define PHY_BASEADDR_CLKGEN23 (0xC00AA000) /* UART_2 */ +#define PHY_BASEADDR_CLKGEN24 (0xC00A8000) /* UART_1 */ +#define PHY_BASEADDR_CLKGEN25 (0xC00AB000) /* UART_3 */ +#define PHY_BASEADDR_CLKGEN26 (0xC006E000) /* UART_4 */ +#define PHY_BASEADDR_CLKGEN27 (0xC00B1000) /* UART_5 */ +#define PHY_BASEADDR_CLKGEN28 (0xC00B5000) /* DEINTERLACE */ +#define PHY_BASEADDR_CLKGEN29 (0xC00C4000) /* PPM */ +#define PHY_BASEADDR_CLKGEN30 (0xC00C1000) /* VIP_0 */ +#define PHY_BASEADDR_CLKGEN31 (0xC00C2000) /* VIP_1 */ +#define PHY_BASEADDR_CLKGEN32 (0xC006B000) /* USB2HOST */ +#define PHY_BASEADDR_CLKGEN33 (0xC00C7000) /* CODA */ +#define PHY_BASEADDR_CLKGEN34 (0xC00C6000) /* CRYPTO */ +#define PHY_BASEADDR_CLKGEN35 (0xC00B6000) /* SCALER */ +#define PHY_BASEADDR_CLKGEN36 (0xC00CB000) /* PDM */ +#define PHY_BASEADDR_CLKGEN37 (0xC00AC000) /* SPI0 */ +#define PHY_BASEADDR_CLKGEN38 (0xC00AD000) /* SPI1 */ +#define PHY_BASEADDR_CLKGEN39 (0xC00A7000) /* SPI2 */ +#if defined(CONFIG_ARCH_S5P6818) +#define PHY_BASEADDR_CLKGEN40 (0xC009A000) +#endif +#define PHY_BASEADDR_DREX (0xC00E0000) + +#define PHY_BASEADDR_CS_NAND (0x2C000000) + +#define PHY_BASEADDR_SRAM (0xFFFF0000) + +/* + * Nexell clock generator + */ +#define CLK_ID_TIMER_1 0 +#define CLK_ID_TIMER_2 1 +#define CLK_ID_TIMER_3 2 +#define CLK_ID_PWM_1 3 +#define CLK_ID_PWM_2 4 +#define CLK_ID_PWM_3 5 +#define CLK_ID_I2C_0 6 +#define CLK_ID_I2C_1 7 +#define CLK_ID_I2C_2 8 +#define CLK_ID_MIPI 9 +#define CLK_ID_GMAC 10 /* External Clock 1 */ +#define CLK_ID_SPDIF_TX 11 +#define CLK_ID_MPEGTSI 12 +#define CLK_ID_PWM_0 13 +#define CLK_ID_TIMER_0 14 +#define CLK_ID_I2S_0 15 /* External Clock 1 */ +#define CLK_ID_I2S_1 16 /* External Clock 1 */ +#define CLK_ID_I2S_2 17 /* External Clock 1 */ +#define CLK_ID_SDHC_0 18 +#define CLK_ID_SDHC_1 19 +#define CLK_ID_SDHC_2 20 +#define CLK_ID_MALI 21 +#define CLK_ID_UART_0 22 /* UART0_MODULE */ +#define CLK_ID_UART_2 23 /* UART1_MODULE */ +#define CLK_ID_UART_1 24 /* pl01115_Uart_modem_MODULE */ +#define CLK_ID_UART_3 25 /* pl01115_Uart_nodma0_MODULE */ +#define CLK_ID_UART_4 26 /* pl01115_Uart_nodma1_MODULE */ +#define CLK_ID_UART_5 27 /* pl01115_Uart_nodma2_MODULE */ +#define CLK_ID_DIT 28 +#define CLK_ID_PPM 29 +#define CLK_ID_VIP_0 30 /* External Clock 1 */ +#define CLK_ID_VIP_1 31 /* External Clock 1, 2 */ +#define CLK_ID_USB2HOST 32 /* External Clock 2 */ +#define CLK_ID_CODA 33 +#define CLK_ID_CRYPTO 34 +#define CLK_ID_SCALER 35 +#define CLK_ID_PDM 36 +#define CLK_ID_SPI_0 37 +#define CLK_ID_SPI_1 38 +#define CLK_ID_SPI_2 39 +#define CLK_ID_MAX 39 + +/* + * Nexell Reset control + */ +#define RESET_ID_AC97 0 +#define RESET_ID_CPU1 1 +#define RESET_ID_CPU2 2 +#define RESET_ID_CPU3 3 +#define RESET_ID_WD1 4 +#define RESET_ID_WD2 5 +#define RESET_ID_WD3 6 +#define RESET_ID_CRYPTO 7 +#define RESET_ID_DEINTERLACE 8 +#define RESET_ID_DISP_TOP 9 +#define RESET_ID_DISPLAY 10 +#define RESET_ID_RESCONV 11 +#define RESET_ID_LCDIF 12 +#define RESET_ID_HDMI 13 +#define RESET_ID_HDMI_VIDEO 14 +#define RESET_ID_HDMI_SPDIF 15 +#define RESET_ID_HDMI_TMDS 16 +#define RESET_ID_HDMI_PHY 17 +#define RESET_ID_LVDS 18 +#define RESET_ID_ECID 19 +#define RESET_ID_I2C0 20 +#define RESET_ID_I2C1 21 +#define RESET_ID_I2C2 22 +#define RESET_ID_I2S0 23 +#define RESET_ID_I2S1 24 +#define RESET_ID_I2S2 25 +#define RESET_ID_DREX_C 26 +#define RESET_ID_DREX_A 27 +#define RESET_ID_DREX 28 +#define RESET_ID_MIPI 29 +#define RESET_ID_MIPI_DSI 30 +#define RESET_ID_MIPI_CSI 31 +#define RESET_ID_MIPI_PHY_S 32 +#define RESET_ID_MIPI_PHY_M 33 +#define RESET_ID_MPEGTSI 34 +#define RESET_ID_PDM 35 +#define RESET_ID_TIMER 36 +#define RESET_ID_PWM 37 +#define RESET_ID_SCALER 38 +#define RESET_ID_SDMMC0 39 +#define RESET_ID_SDMMC1 40 +#define RESET_ID_SDMMC2 41 +#define RESET_ID_SPDIFRX 42 +#define RESET_ID_SPDIFTX 43 +#define RESET_ID_SSP0_P 44 +#define RESET_ID_SSP0 45 +#define RESET_ID_SSP1_P 46 +#define RESET_ID_SSP1 47 +#define RESET_ID_SSP2_P 48 +#define RESET_ID_SSP2 49 +#define RESET_ID_UART0 50 /* UART1 */ +#define RESET_ID_UART1 51 /* pl01115_Uart_modem */ +#define RESET_ID_UART2 52 /* UART1 */ +#define RESET_ID_UART3 53 /* pl01115_Uart_nodma0 */ +#define RESET_ID_UART4 54 /* pl01115_Uart_nodma1 */ +#define RESET_ID_UART5 55 /* pl01115_Uart_nodma2 */ +#define RESET_ID_USB20HOST 56 +#define RESET_ID_USB20OTG 57 +#define RESET_ID_WDT 58 +#define RESET_ID_WDT_POR 59 +#define RESET_ID_ADC 60 +#define RESET_ID_CODA_A 61 +#define RESET_ID_CODA_P 62 +#define RESET_ID_CODA_C 63 +#define RESET_ID_DWC_GMAC 64 +#define RESET_ID_MALI400 65 +#define RESET_ID_PPM 66 +#define RESET_ID_VIP1 67 +#define RESET_ID_VIP0 68 +#if defined(CONFIG_ARCH_S5P6818) +#define RESET_ID_VIP2 69 +#endif + +/* + * device name + */ +#define DEV_NAME_UART "nx-uart" /* pl0115 (amba-pl011.c) */ +#define DEV_NAME_FB "nx-fb" +#define DEV_NAME_DISP "nx-disp" +#define DEV_NAME_LCD "nx-lcd" +#define DEV_NAME_LVDS "nx-lvds" +#define DEV_NAME_HDMI "nx-hdmi" +#define DEV_NAME_RESCONV "nx-resconv" +#define DEV_NAME_MIPI "nx-mipi" +#define DEV_NAME_PCM "nx-pcm" +#define DEV_NAME_I2S "nx-i2s" +#define DEV_NAME_SPDIF_TX "nx-spdif-tx" +#define DEV_NAME_SPDIF_RX "nx-spdif-rx" +#define DEV_NAME_I2C "nx-i2c" +#define DEV_NAME_NAND "nx-nand" +#define DEV_NAME_KEYPAD "nx-keypad" +#define DEV_NAME_SDHC "nx-sdhc" +#define DEV_NAME_PWM "nx-pwm" +#define DEV_NAME_TIMER "nx-timer" +#define DEV_NAME_SOC_PWM "nx-soc-pwm" +#define DEV_NAME_GPIO "nx-gpio" +#define DEV_NAME_RTC "nx-rtc" +#define DEV_NAME_GMAC "nx-gmac" +#define DEV_NAME_MPEGTSI "nx-mpegtsi" +#define DEV_NAME_MALI "nx-mali" +#define DEV_NAME_DIT "nx-deinterlace" +#define DEV_NAME_PPM "nx-ppm" +#define DEV_NAME_VIP "nx-vip" +#define DEV_NAME_CODA "nx-coda" +#define DEV_NAME_USB2HOST "nx-usb2h" +#define DEV_NAME_CRYPTO "nx-crypto" +#define DEV_NAME_SCALER "nx-scaler" +#define DEV_NAME_PDM "nx-pdm" +#define DEV_NAME_SPI "nx-spi" +#define DEV_NAME_CPUFREQ "nx-cpufreq" + +/* + * clock generator + */ +#define CORECLK_NAME_PLL0 "pll0" /* cpu clock */ +#define CORECLK_NAME_PLL1 "pll1" +#define CORECLK_NAME_PLL2 "pll2" +#define CORECLK_NAME_PLL3 "pll3" +#define CORECLK_NAME_FCLK "fclk" +#define CORECLK_NAME_MCLK "mclk" +#define CORECLK_NAME_BCLK "bclk" +#define CORECLK_NAME_PCLK "pclk" +#define CORECLK_NAME_HCLK "hclk" + +#define CORECLK_ID_PLL0 0 +#define CORECLK_ID_PLL1 1 +#define CORECLK_ID_PLL2 2 +#define CORECLK_ID_PLL3 3 +#define CORECLK_ID_FCLK 4 +#define CORECLK_ID_MCLK 5 +#define CORECLK_ID_BCLK 6 +#define CORECLK_ID_PCLK 7 +#define CORECLK_ID_HCLK 8 + +#define ALIVEPWRGATEREG (PHY_BASEADDR_ALIVE + 0x0) + +#if defined(CONFIG_ARCH_S5P4418) +#define SCR_ARM_SECOND_BOOT (0xC0010C1C) /* PWR scratch */ +#define SCR_ARM_SECOND_BOOT_REG1 (0xc0010234) /* ToDo : Check Address */ +#elif defined(CONFIG_ARCH_S5P6818) +#define SCR_ARM_SECOND_BOOT (0xc0010230) /* PWR scratch */ +#define SCR_ARM_SECOND_BOOT_REG1 (0xc0010234) /* PWR scratch */ +#define SCR_ARM_SECOND_BOOT_REG2 (0xc0010238) /* PWR scratch */ +#endif + +#define SCR_ALIVE_BASE (PHY_BASEADDR_ALIVE) +#define SCR_SIGNAGURE_RESET (SCR_ALIVE_BASE + 0x068) +#define SCR_SIGNAGURE_SET (SCR_ALIVE_BASE + 0x06C) +#define SCR_SIGNAGURE_READ (SCR_ALIVE_BASE + 0x070) + +#define SYSRSTCONFIG (0x23C) +#define DEVICEBOOTINFO (0x50) +#define BOOTMODE_MASK (0x7) +#define BOOTMODE_SDMMC 5 +#define BOOTMODE_USB 6 +#define BOOTMODE_SDMMC_PORT_VAL(x) ((((x) >> 3) & 1) | \ + (((x) >> 19 & 1) << 1)) +#define EMMC_PORT_NUM 2 +#define SD_PORT_NUM 0 +#define ID_REG_EC0 (0x54) +#define WIRE0_MASK (0x1) + +#ifndef __ASSEMBLY__ + +#define NS_IN_HZ (1000000000UL) +#define TO_PERIOD_NS(freq) (NS_IN_HZ / (freq)) +#define TO_DUTY_NS(duty, freq) (duty ? TO_PERIOD_NS(freq) / (100 / duty) : 0) + +#endif /* __ASSEMBLY__ */ + +#endif /* __NEXELL_H__ */ diff --git a/arch/arm/mach-nexell/include/mach/nx_gpio.h b/arch/arm/mach-nexell/include/mach/nx_gpio.h new file mode 100644 index 0000000..91803d2 --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/nx_gpio.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2016 Nexell + * Youngbok, Park ybpark@nexell.co.kr + */ + +#include <linux/types.h> +#include <asm/io.h> + +#ifndef __nx_gpio_h__ +#define __nx_gpio_h__ + +struct nx_gpio_register_set { + u32 gpioxout; + u32 gpioxoutenb; + u32 gpioxdetmode[2]; + u32 gpioxintenb; + u32 gpioxdet; + u32 gpioxpad; + u32 gpioxpuenb; + u32 gpioxaltfn[2]; + u32 gpioxdetmodeex; + u32 __reserved[4]; + u32 gpioxdetenb; + u32 gpiox_slew; + u32 gpiox_slew_disable_default; + u32 gpiox_drv1; + u32 gpiox_drv1_disable_default; + u32 gpiox_drv0; + u32 gpiox_drv0_disable_default; + u32 gpiox_pullsel; + u32 gpiox_pullsel_disable_default; + u32 gpiox_pullenb; + u32 gpiox_pullenb_disable_default; + u32 gpiox_input_mux_select0; + u32 gpiox_input_mux_select1; + u8 __reserved1[0x1000 - 0x70]; +}; + +enum { + nx_gpio_padfunc_0 = 0ul, + nx_gpio_padfunc_1 = 1ul, + nx_gpio_padfunc_2 = 2ul, + nx_gpio_padfunc_3 = 3ul +}; + +enum { + nx_gpio_drvstrength_0 = 0ul, + nx_gpio_drvstrength_1 = 1ul, + nx_gpio_drvstrength_2 = 2ul, + nx_gpio_drvstrength_3 = 3ul +}; + +enum { + nx_gpio_pull_down = 0ul, + nx_gpio_pull_up = 1ul, + nx_gpio_pull_off = 2ul +}; + +int nx_gpio_initialize(void); +u32 nx_gpio_get_number_of_module(void); +u32 nx_gpio_get_size_of_register_set(void); +void nx_gpio_set_base_address(u32 module_index, void *base_address); +void *nx_gpio_get_base_address(u32 module_index); +int nx_gpio_open_module(u32 module_index); +int nx_gpio_close_module(u32 module_index); +int nx_gpio_check_busy(u32 module_index); +void nx_gpio_set_detect_enable(u32 module_index, u32 bit_number, + int detect_enb); +void nx_gpio_set_pad_function(u32 module_index, u32 bit_number, u32 padfunc); +void nx_gpio_set_pad_function32(u32 module_index, u32 msbvalue, u32 lsbvalue); +int nx_gpio_get_pad_function(u32 module_index, u32 bit_number); +void nx_gpio_set_output_enable(u32 module_index, u32 bit_number, + int output_enb); +int nx_gpio_get_detect_enable(u32 module_index, u32 bit_number); +u32 nx_gpio_get_detect_enable32(u32 module_index); +void nx_gpio_set_detect_enable(u32 module_index, u32 bit_number, + int detect_enb); +void nx_gpio_set_detect_enable32(u32 module_index, u32 enable_flag); +int nx_gpio_get_output_enable(u32 module_index, u32 bit_number); +void nx_gpio_set_output_enable32(u32 module_index, int output_enb); +u32 nx_gpio_get_output_enable32(u32 module_index); +void nx_gpio_set_output_value(u32 module_index, u32 bit_number, int value); +int nx_gpio_get_output_value(u32 module_index, u32 bit_number); +void nx_gpio_set_output_value32(u32 module_index, u32 value); +u32 nx_gpio_get_output_value32(u32 module_index); +int nx_gpio_get_input_value(u32 module_index, u32 bit_number); +void nx_gpio_set_pull_select(u32 module_index, u32 bit_number, int enable); +void nx_gpio_set_pull_select32(u32 module_index, u32 value); +int nx_gpio_get_pull_select(u32 module_index, u32 bit_number); +u32 nx_gpio_get_pull_select32(u32 module_index); +void nx_gpio_set_pull_mode(u32 module_index, u32 bit_number, u32 mode); +void nx_gpio_set_fast_slew(u32 module_index, u32 bit_number, int enable); +void nx_gpio_set_drive_strength_disable_default(u32 module_index, + u32 bit_number, int enable); +void nx_gpio_set_drive_strength_disable_default(u32 module_index, + u32 bit_number, int enable); +void nx_gpio_set_drive_strength(u32 module_index, u32 bit_number, + u32 drvstrength); +void nx_gpio_set_drive_strength_disable_default(u32 module_index, + u32 bit_number, int enable); +u32 nx_gpio_get_drive_strength(u32 module_index, u32 bit_number); +#endif diff --git a/arch/arm/mach-nexell/include/mach/reset.h b/arch/arm/mach-nexell/include/mach/reset.h new file mode 100644 index 0000000..e1301d4 --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/reset.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2016 Nexell + * Youngbok, Park ybpark@nexell.co.kr + */ + +#ifndef __NEXELL_RESET__ +#define __NEXELL_RESET__ + +#define NUMBER_OF_RESET_MODULE_PIN 69 + +enum rstcon { + RSTCON_ASSERT = 0UL, + RSTCON_NEGATE = 1UL +}; + +void nx_rstcon_setrst(u32 rstindex, enum rstcon status); + +#endif /* __NEXELL_RESET__ */ diff --git a/arch/arm/mach-nexell/include/mach/sec_reg.h b/arch/arm/mach-nexell/include/mach/sec_reg.h new file mode 100644 index 0000000..e3ae5ac --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/sec_reg.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2016 Nexell + * Youngbok, Park park@nexell.co.kr + */ + +#define NEXELL_L2C_SEC_ID 0 +#define NEXELL_MALI_SEC_ID 2 +#define NEXELL_MIPI_SEC_ID 4 +#define NEXELL_TOFF_SEC_ID 6 + +int write_sec_reg_by_id(void __iomem *reg, int val, int id); +int read_sec_reg_by_id(void __iomem *reg, int id); +int read_sec_reg(void __iomem *reg); +int write_sec_reg(void __iomem *reg, int val); diff --git a/arch/arm/mach-nexell/include/mach/tieoff.h b/arch/arm/mach-nexell/include/mach/tieoff.h new file mode 100644 index 0000000..70e9652 --- /dev/null +++ b/arch/arm/mach-nexell/include/mach/tieoff.h @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * (C) Copyright 2016 Nexell + * Youngbok, Park park@nexell.co.kr + */ + +#ifndef _NEXELL_TIEOFF_H +#define _NEXELL_TIEOFF_H + +void nx_tieoff_set(u32 tieoff_index, u32 tieoff_value); +u32 nx_tieoff_get(u32 tieoff_index); + +#if defined(CONFIG_ARCH_S5P4418) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CLAMPCOREOUT ((1 << 16) | 0) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CLAMPCPU0 ((1 << 16) | 1) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CLAMPCPU1 ((1 << 16) | 2) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CLAMPCPU2 ((1 << 16) | 3) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CLAMPCPU3 ((1 << 16) | 4) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_COREPWRDOWN ((1 << 16) | 5) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CPU0PWRDOWN ((1 << 16) | 6) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CPU1PWRDOWN ((1 << 16) | 7) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CPU2PWRDOWN ((1 << 16) | 8) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CPU3PWRDOWN ((1 << 16) | 9) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2_CFGENDIAN ((1 << 16) | 10) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L1EMAS ((1 << 16) | 11) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2RET1N_0 ((1 << 16) | 12) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2RET1N_1 ((1 << 16) | 13) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2PGEN_0 ((1 << 16) | 14) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2PGEN_1 ((1 << 16) | 15) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CLAMPL2_0 ((1 << 16) | 16) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CLAMPL2_1 ((1 << 16) | 17) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_VINITHI ((4 << 16) | 18) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2EMA ((3 << 16) | 22) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_TEINIT ((4 << 16) | 25) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L1EMAW ((2 << 16) | 29) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2EMAW ((2 << 16) | 32) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L1EMA ((3 << 16) | 34) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_CPUCLKOFF ((4 << 16) | 37) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_PWRCTLI0 ((2 << 16) | 41) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_PWRCTLI1 ((2 << 16) | 43) +#define NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_PWRCTLI2 ((2 << 16) | 45) +#elif defined(CONFIG_ARCH_S5P6818) +#define NX_TIEOFF_MMC_8BIT ((1 << 16) | 5) +#endif +#define NX_TIEOFF_AXISRAM0_i_TIE_ra2w_EMAA ((3 << 16) | 47) +#define NX_TIEOFF_AXISRAM0_i_TIE_ra2w_EMAB ((3 << 16) | 50) +#define NX_TIEOFF_AXISRAM0_i_TIE_ra2w_EMAWA ((2 << 16) | 53) +#define NX_TIEOFF_AXISRAM0_i_TIE_ra2w_EMAWB ((2 << 16) | 55) +#define NX_TIEOFF_AXISRAM0_i_nPowerDown ((1 << 16) | 57) +#define NX_TIEOFF_AXISRAM0_i_nSleep ((1 << 16) | 58) +#define NX_TIEOFF_CAN0_i_TIE_rf1_EMA ((3 << 16) | 59) +#define NX_TIEOFF_CAN0_i_TIE_rf1_EMAW ((2 << 16) | 62) +#define NX_TIEOFF_CAN0_i_nPowerDown ((1 << 16) | 64) +#define NX_TIEOFF_CAN0_i_nSleep ((1 << 16) | 65) +#define NX_TIEOFF_CAN1_i_TIE_rf1_EMA ((3 << 16) | 66) +#define NX_TIEOFF_CAN1_i_TIE_rf1_EMAW ((2 << 16) | 69) +#define NX_TIEOFF_CAN1_i_nPowerDown ((1 << 16) | 71) +#define NX_TIEOFF_CAN1_i_nSleep ((1 << 16) | 72) +#define NX_TIEOFF_DEINTERLACE0_i_NX_RF1_EMA ((3 << 16) | 73) +#define NX_TIEOFF_DEINTERLACE0_i_NX_RF1_EMAW ((2 << 16) | 76) +#define NX_TIEOFF_DEINTERLACE0_i_NX_RF2_EMAA ((3 << 16) | 78) +#define NX_TIEOFF_DEINTERLACE0_i_NX_RF2_EMAB ((3 << 16) | 81) +#define NX_TIEOFF_DEINTERLACE0_i_NX_RF2W_EMAA ((3 << 16) | 84) +#define NX_TIEOFF_DEINTERLACE0_i_NX_RF2W_EMAB ((3 << 16) | 87) +#define NX_TIEOFF_DISPLAYTOP0_i_ResConv_nPowerDown ((1 << 16) | 90) +#define NX_TIEOFF_DISPLAYTOP0_i_ResConv_nSleep ((1 << 16) | 91) +#define NX_TIEOFF_DISPLAYTOP0_i_HDMI_nPowerDown ((2 << 16) | 92) +#define NX_TIEOFF_DISPLAYTOP0_i_HDMI_nSleep ((2 << 16) | 94) +#define NX_TIEOFF_DISPLAYTOP0_i_HDMI_PHY_REFCLK_SEL ((1 << 16) | 96) +#define NX_TIEOFF_DISPLAYTOP0_i_TIEOFF_SPSRAM_EMA ((3 << 16) | 97) +#define NX_TIEOFF_DISPLAYTOP0_i_TIEOFF_SPSRAM_EMAW ((2 << 16) | 100) +#define NX_TIEOFF_DISPLAYTOP0_i_TIEOFF_DPSRAM_1R1W_EMAA ((3 << 16) | 102) +#define NX_TIEOFF_DISPLAYTOP0_i_TIEOFF_DPSRAM_1R1W_EMAB ((3 << 16) | 105) +#define NX_TIEOFF_DISPLAYTOP0_i_TIEOFF_DPSRAM_EMAA ((3 << 16) | 108) +#define NX_TIEOFF_DISPLAYTOP0_i_TIEOFF_DPSRAM_EMAB ((3 << 16) | 111) +#define NX_TIEOFF_DISPLAYTOP0_i_TIEOFF_DPSRAM_EMAWA ((2 << 16) | 114) +#define NX_TIEOFF_DISPLAYTOP0_i_TIEOFF_DPSRAM_EMAWB ((2 << 16) | 116) +#define NX_TIEOFF_MCUSTOP0_i_vrom_EMA ((3 << 16) | 118) +#define NX_TIEOFF_DREX0_CKE_INIT ((1 << 16) | 121) +#define NX_TIEOFF_DREX0_CA_SWAP ((1 << 16) | 122) +#define NX_TIEOFF_DREX0_CSYSREQ ((1 << 16) | 123) +#define NX_TIEOFF_DREX0_PAUSE_REQ ((1 << 16) | 124) +#define NX_TIEOFF_DREX0_PEREV_TRIGGER ((1 << 16) | 125) +#define NX_TIEOFF_DREX0_CTRL_HCKE ((1 << 16) | 126) +#define NX_TIEOFF_DREX0_DFI_RESET_N_P0 ((1 << 16) | 127) +#define NX_TIEOFF_DREX0_DFI_RESET_N_P1 ((1 << 16) | 128) +#define NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAA ((3 << 16) | 129) +#define NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAB ((3 << 16) | 132) +#define NX_TIEOFF_MIPI0_i_NX_NPOWERDOWN ((4 << 16) | 135) +#define NX_TIEOFF_MIPI0_i_NX_NSLEEP ((4 << 16) | 139) +#define NX_TIEOFF_SCALER0_i_NX_EMA ((3 << 16) | 143) +#define NX_TIEOFF_SCALER0_i_NX_EMAW ((2 << 16) | 146) +#define NX_TIEOFF_UART0_USESMC ((1 << 16) | 148) +#define NX_TIEOFF_UART0_SMCTXENB ((1 << 16) | 149) +#define NX_TIEOFF_UART0_SMCRXENB ((1 << 16) | 150) +#define NX_TIEOFF_UART1_USESMC ((1 << 16) | 151) +#define NX_TIEOFF_UART1_SMCTXENB ((1 << 16) | 152) +#define NX_TIEOFF_UART1_SMCRXENB ((1 << 16) | 153) +#define NX_TIEOFF_UART2_USESMC ((1 << 16) | 154) +#define NX_TIEOFF_UART2_SMCTXENB ((1 << 16) | 155) +#define NX_TIEOFF_UART2_SMCRXENB ((1 << 16) | 156) +#define NX_TIEOFF_UART3_USESMC ((1 << 16) | 157) +#define NX_TIEOFF_UART3_SMCTXENB ((1 << 16) | 158) +#define NX_TIEOFF_UART3_SMCRXENB ((1 << 16) | 159) +#define NX_TIEOFF_UART4_USESMC ((1 << 16) | 160) +#define NX_TIEOFF_UART4_SMCTXENB ((1 << 16) | 161) +#define NX_TIEOFF_UART4_SMCRXENB ((1 << 16) | 162) +#define NX_TIEOFF_UART5_USESMC ((1 << 16) | 163) +#define NX_TIEOFF_UART5_SMCTXENB ((1 << 16) | 164) +#define NX_TIEOFF_UART5_SMCRXENB ((1 << 16) | 165) +#define NX_TIEOFF_USB20HOST0_i_nPowerDown ((1 << 16) | 166) +#define NX_TIEOFF_USB20HOST0_i_nSleep ((1 << 16) | 167) +#define NX_TIEOFF_USB20HOST0_i_NX_RF1_EMA ((3 << 16) | 168) +#define NX_TIEOFF_USB20HOST0_i_NX_RF1_EMAW ((2 << 16) | 171) +#define NX_TIEOFF_USB20HOST0_sys_interrupt_i ((1 << 16) | 173) +#define NX_TIEOFF_USB20HOST0_i_hsic_en ((3 << 16) | 174) +#define NX_TIEOFF_USB20HOST0_i_nResetSync ((1 << 16) | 177) +#define NX_TIEOFF_USB20HOST0_i_nResetSync_ohci ((1 << 16) | 178) +#define NX_TIEOFF_USB20HOST0_i_nAuxWellResetSync ((1 << 16) | 179) +#define NX_TIEOFF_USB20HOST0_i_nHostPhyResetSync ((1 << 16) | 180) +#define NX_TIEOFF_USB20HOST0_i_nHostUtmiResetSync ((1 << 16) | 181) +#define NX_TIEOFF_USB20HOST0_i_nHostHsicResetSync ((1 << 16) | 182) +#define NX_TIEOFF_USB20HOST0_i_HSIC_FREE_CLOCK_ENB ((1 << 16) | 183) +#define NX_TIEOFF_USB20HOST0_i_HSIC_480M_FROM_OTG_PHY ((1 << 16) | 184) +#define NX_TIEOFF_USB20HOST0_ss_word_if_enb_i ((1 << 16) | 185) +#define NX_TIEOFF_USB20HOST0_ss_word_if_i ((1 << 16) | 186) +#define NX_TIEOFF_USB20HOST0_ss_utmi_backward_enb_i ((1 << 16) | 187) +#define NX_TIEOFF_USB20HOST0_ss_resume_utmi_pls_dis_i ((1 << 16) | 188) +#define NX_TIEOFF_USB20HOST0_phy_vstatus_0_i ((3 << 16) | 189) +#define NX_TIEOFF_USB20HOST0_phy_vstatus_1_i ((3 << 16) | 192) +#define NX_TIEOFF_USB20HOST0_phy_vstatus_2_i ((3 << 16) | 195) +#define NX_TIEOFF_USB20HOST0_phy_vstatus_3_i ((3 << 16) | 198) +#define NX_TIEOFF_USB20HOST0_phy_vstatus_4_i ((3 << 16) | 201) +#define NX_TIEOFF_USB20HOST0_phy_vstatus_5_i ((3 << 16) | 204) +#define NX_TIEOFF_USB20HOST0_phy_vstatus_6_i ((3 << 16) | 207) +#define NX_TIEOFF_USB20HOST0_phy_vstatus_7_i ((3 << 16) | 210) +#define NX_TIEOFF_USB20HOST0_ss_power_state_valid_i ((1 << 16) | 213) +#define NX_TIEOFF_USB20HOST0_ss_nxt_power_state_valid_i ((1 << 16) | 214) +#define NX_TIEOFF_USB20HOST0_ss_power_state_i ((2 << 16) | 215) +#define NX_TIEOFF_USB20HOST0_ss_next_power_state_i ((2 << 16) | 217) +#define NX_TIEOFF_USB20HOST0_app_prt_ovrcur_i ((3 << 16) | 219) +#define NX_TIEOFF_USB20HOST0_ss_simulation_mode_i ((1 << 16) | 222) +#define NX_TIEOFF_USB20HOST0_ss_fladj_val_host_i ((6 << 16) | 224) +#define NX_TIEOFF_USB20HOST0_ss_fladj_val_5_i ((3 << 16) | 230) +#define NX_TIEOFF_USB20HOST0_ss_fladj_val_4_i ((3 << 16) | 233) +#define NX_TIEOFF_USB20HOST0_ss_fladj_val_3_i ((3 << 16) | 236) +#define NX_TIEOFF_USB20HOST0_ss_fladj_val_2_i ((3 << 16) | 239) +#define NX_TIEOFF_USB20HOST0_ss_fladj_val_1_i ((3 << 16) | 242) +#define NX_TIEOFF_USB20HOST0_ss_fladj_val_0_i ((3 << 16) | 245) +#define NX_TIEOFF_USB20HOST0_ss_autoppd_on_overcur_en_i ((1 << 16) | 248) +#define NX_TIEOFF_USB20HOST0_ss_ena_incr16_i ((1 << 16) | 249) +#define NX_TIEOFF_USB20HOST0_ss_ena_incr8_i ((1 << 16) | 250) +#define NX_TIEOFF_USB20HOST0_ss_ena_incr4_i ((1 << 16) | 251) +#define NX_TIEOFF_USB20HOST0_ss_ena_incrx_align_i ((1 << 16) | 252) +#define NX_TIEOFF_USB20HOST0_i_ohci_0_cntsel_n ((1 << 16) | 253) +#define NX_TIEOFF_USB20HOST0_ohci_0_app_irq1_i ((1 << 16) | 254) +#define NX_TIEOFF_USB20HOST0_ohci_0_app_irq12_i ((1 << 16) | 255) +#define NX_TIEOFF_USB20HOST0_ohci_0_app_io_hit_i ((1 << 16) | 256) +#define NX_TIEOFF_USB20HOST0_ss_hubsetup_min_i ((1 << 16) | 257) +#define NX_TIEOFF_USB20HOST0_app_start_clk_i ((1 << 16) | 258) +#define NX_TIEOFF_USB20HOST0_ohci_susp_lgcy_i ((1 << 16) | 259) +#define NX_TIEOFF_USB20HOST0_i_SIDDQ ((1 << 16) | 260) +#define NX_TIEOFF_USB20HOST0_i_VATESTENB ((2 << 16) | 261) +#define NX_TIEOFF_USB20HOST0_i_POR_ENB ((1 << 16) | 263) +#define NX_TIEOFF_USB20HOST0_i_POR ((1 << 16) | 264) +#define NX_TIEOFF_USB20HOST0_i_REFCLKSEL ((2 << 16) | 265) +#define NX_TIEOFF_USB20HOST0_i_FSEL ((3 << 16) | 267) +#define NX_TIEOFF_USB20HOST0_i_COMMONONN ((1 << 16) | 270) +#define NX_TIEOFF_USB20HOST0_i_RESREQIN ((1 << 16) | 271) +#define NX_TIEOFF_USB20HOST0_i_PORTRESET ((1 << 16) | 272) +#define NX_TIEOFF_USB20HOST0_i_OTGDISABLE ((1 << 16) | 273) +#define NX_TIEOFF_USB20HOST0_i_LOOPBACKENB ((1 << 16) | 274) +#define NX_TIEOFF_USB20HOST0_i_IDPULLUPi ((1 << 16) | 275) +#define NX_TIEOFF_USB20HOST0_i_DRVVBUS ((1 << 16) | 276) +#define NX_TIEOFF_USB20HOST0_i_ADPCHRG ((1 << 16) | 277) +#define NX_TIEOFF_USB20HOST0_i_ADPDISCHRG ((1 << 16) | 278) +#define NX_TIEOFF_USB20HOST0_i_ADPPRBENB ((1 << 16) | 279) +#define NX_TIEOFF_USB20HOST0_i_VBUSVLDEXT ((1 << 16) | 280) +#define NX_TIEOFF_USB20HOST0_i_VBUSVLDEXTSEL ((1 << 16) | 281) +#define NX_TIEOFF_USB20HOST0_i_DPPULLDOWN ((1 << 16) | 282) +#define NX_TIEOFF_USB20HOST0_i_DMPULLDOWN ((1 << 16) | 283) +#define NX_TIEOFF_USB20HOST0_i_SUSPENDM_ENB ((1 << 16) | 284) +#define NX_TIEOFF_USB20HOST0_i_SUSPENDM ((1 << 16) | 285) +#define NX_TIEOFF_USB20HOST0_i_SLEEPM_ENB ((1 << 16) | 286) +#define NX_TIEOFF_USB20HOST0_i_SLEEPM ((1 << 16) | 287) +#define NX_TIEOFF_USB20HOST0_i_OPMODE_ENB ((1 << 16) | 288) +#define NX_TIEOFF_USB20HOST0_i_OPMODE ((2 << 16) | 289) +#define NX_TIEOFF_USB20HOST0_i_TERMSEL_ENB ((1 << 16) | 291) +#define NX_TIEOFF_USB20HOST0_i_TERMSEL ((1 << 16) | 292) +#define NX_TIEOFF_USB20HOST0_i_XCVRSEL_ENB ((1 << 16) | 293) +#define NX_TIEOFF_USB20HOST0_i_XCVRSEL ((2 << 16) | 294) +#define NX_TIEOFF_USB20HOST0_i_WORDINTERFACE_ENB ((1 << 16) | 296) +#define NX_TIEOFF_USB20HOST0_i_WORDINTERFACE ((1 << 16) | 297) +#define NX_TIEOFF_USB20HOST0_i_TXBITSTUFFEN ((1 << 16) | 298) +#define NX_TIEOFF_USB20HOST0_i_TXBITSTUFFENH ((1 << 16) | 299) +#define NX_TIEOFF_USB20HOST0_i_BYPASSDPDATA ((1 << 16) | 300) +#define NX_TIEOFF_USB20HOST0_i_BYPASSDMDATA ((1 << 16) | 301) +#define NX_TIEOFF_USB20HOST0_i_BYPASSDPEN ((1 << 16) | 302) +#define NX_TIEOFF_USB20HOST0_i_BYPASSDMEN ((1 << 16) | 303) +#define NX_TIEOFF_USB20HOST0_i_BYPASSSEL ((1 << 16) | 304) +#define NX_TIEOFF_USB20HOST0_i_COMPDISTUNE ((3 << 16) | 305) +#define NX_TIEOFF_USB20HOST0_i_SQRXTUNE ((3 << 16) | 308) +#define NX_TIEOFF_USB20HOST0_i_OTGTUNE ((3 << 16) | 311) +#define NX_TIEOFF_USB20HOST0_i_TXHSXVTUNE ((2 << 16) | 314) +#define NX_TIEOFF_USB20HOST0_i_TXFSLSTUNE ((4 << 16) | 316) +#define NX_TIEOFF_USB20HOST0_i_TXVREFTUNE ((4 << 16) | 320) +#define NX_TIEOFF_USB20HOST0_i_TXRISETUNE ((2 << 16) | 324) +#define NX_TIEOFF_USB20HOST0_i_TXRESTUNE ((2 << 16) | 326) +#define NX_TIEOFF_USB20HOST0_i_TXPREEMPAMPTUNE ((2 << 16) | 328) +#define NX_TIEOFF_USB20HOST0_i_TXPREEMPPULSETUNE ((1 << 16) | 330) +#define NX_TIEOFF_USB20HOST0_i_CHRGSEL ((1 << 16) | 331) +#define NX_TIEOFF_USB20HOST0_i_VDATDETENB ((1 << 16) | 332) +#define NX_TIEOFF_USB20HOST0_i_VDATSRCENB ((1 << 16) | 333) +#define NX_TIEOFF_USB20HOST0_i_DCDENB ((1 << 16) | 334) +#define NX_TIEOFF_USB20HOST0_i_ACAENB ((1 << 16) | 335) +#define NX_TIEOFF_USB20HOST0_i_HSIC_MSTRXCVR ((1 << 16) | 336) +#define NX_TIEOFF_USB20HOST0_i_HSIC_SIDDQ ((1 << 16) | 337) +#define NX_TIEOFF_USB20HOST0_i_HSIC_POR_ENB ((1 << 16) | 338) +#define NX_TIEOFF_USB20HOST0_i_HSIC_POR ((1 << 16) | 339) +#define NX_TIEOFF_USB20HOST0_i_HSIC_REFCLKDIV ((7 << 16) | 340) +#define NX_TIEOFF_USB20HOST0_i_HSIC_REFCLKSEL ((2 << 16) | 347) +#define NX_TIEOFF_USB20HOST0_i_HSIC_COMMONONN ((1 << 16) | 349) +#define NX_TIEOFF_USB20HOST0_i_HSIC_PORTRESET ((1 << 16) | 350) +#define NX_TIEOFF_USB20HOST0_i_HSIC_LOOPBACKENB ((1 << 16) | 351) +#define NX_TIEOFF_USB20HOST0_i_HSIC_DPPULLDOWN ((1 << 16) | 352) +#define NX_TIEOFF_USB20HOST0_i_HSIC_DMPULLDOWN ((1 << 16) | 353) +#define NX_TIEOFF_USB20HOST0_i_HSIC_SUSPENDM_ENB ((1 << 16) | 354) +#define NX_TIEOFF_USB20HOST0_i_HSIC_SUSPENDM ((1 << 16) | 355) +#define NX_TIEOFF_USB20HOST0_i_HSIC_SLEEPM_ENB ((1 << 16) | 356) +#define NX_TIEOFF_USB20HOST0_i_HSIC_SLEEPM ((1 << 16) | 357) +#define NX_TIEOFF_USB20HOST0_i_HSIC_MSTRXOPU ((1 << 16) | 358) +#define NX_TIEOFF_USB20HOST0_i_HSIC_OPMODE_ENB ((1 << 16) | 359) +#define NX_TIEOFF_USB20HOST0_i_HSIC_OPMODE ((2 << 16) | 360) +#define NX_TIEOFF_USB20HOST0_i_HSIC_XCVRSELECT_ENB ((1 << 16) | 362) +#define NX_TIEOFF_USB20HOST0_i_HSIC_XCVRSELECT ((1 << 16) | 363) +#define NX_TIEOFF_USB20HOST0_i_HSIC_WORDINTERFACE_ENB ((1 << 16) | 364) +#define NX_TIEOFF_USB20HOST0_i_HSIC_WORDINTERFACE ((1 << 16) | 365) +#define NX_TIEOFF_USB20HOST0_i_HSIC_TXBITSTUFFEN ((1 << 16) | 366) +#define NX_TIEOFF_USB20HOST0_i_HSIC_TXBITSTUFFENH ((1 << 16) | 367) +#define NX_TIEOFF_USB20HOST0_i_HSIC_TXRPUTUNE ((2 << 16) | 368) +#define NX_TIEOFF_USB20HOST0_i_HSIC_TXRPDTUNE ((2 << 16) | 370) +#define NX_TIEOFF_USB20HOST0_i_HSIC_TXSRTUNE ((4 << 16) | 372) +#define NX_TIEOFF_USB20OTG0_i_nPowerDown ((1 << 16) | 376) +#define NX_TIEOFF_USB20OTG0_i_nSleep ((1 << 16) | 377) +#define NX_TIEOFF_USB20OTG0_i_NX_RF1_EMA ((3 << 16) | 378) +#define NX_TIEOFF_USB20OTG0_i_NX_RF1_EMAW ((2 << 16) | 381) +#define NX_TIEOFF_USB20OTG0_i_ss_scaledown_mode ((2 << 16) | 384) +#define NX_TIEOFF_USB20OTG0_i_gp_in ((16 << 16) | 386) +#define NX_TIEOFF_USB20OTG0_i_sof_count ((14 << 16) | 402) +#define NX_TIEOFF_USB20OTG0_i_sys_dma_done ((1 << 16) | 416) +#define NX_TIEOFF_USB20OTG0_i_if_select_hsic ((1 << 16) | 417) +#define NX_TIEOFF_USB20OTG0_i_nResetSync ((1 << 16) | 418) +#define NX_TIEOFF_USB20OTG0_i_nUtmiResetSync ((1 << 16) | 419) +#define NX_TIEOFF_USB20OTG0_i_SIDDQ ((1 << 16) | 420) +#define NX_TIEOFF_USB20OTG0_i_VATESTENB ((2 << 16) | 421) +#define NX_TIEOFF_USB20OTG0_i_POR_ENB ((1 << 16) | 423) +#define NX_TIEOFF_USB20OTG0_i_POR ((1 << 16) | 424) +#define NX_TIEOFF_USB20OTG0_i_REFCLKSEL ((2 << 16) | 425) +#define NX_TIEOFF_USB20OTG0_i_FSEL ((3 << 16) | 427) +#define NX_TIEOFF_USB20OTG0_i_COMMONONN ((1 << 16) | 430) +#define NX_TIEOFF_USB20OTG0_i_RESREQIN ((1 << 16) | 431) +#define NX_TIEOFF_USB20OTG0_i_PORTRESET ((1 << 16) | 432) +#define NX_TIEOFF_USB20OTG0_i_OTGDISABLE ((1 << 16) | 433) +#define NX_TIEOFF_USB20OTG0_i_LOOPBACKENB ((1 << 16) | 434) +#define NX_TIEOFF_USB20OTG0_i_IDPULLUP ((1 << 16) | 435) +#define NX_TIEOFF_USB20OTG0_i_DRVVBUS ((1 << 16) | 436) +#define NX_TIEOFF_USB20OTG0_i_ADPCHRG ((1 << 16) | 437) +#define NX_TIEOFF_USB20OTG0_i_ADPDISCHRG ((1 << 16) | 438) +#define NX_TIEOFF_USB20OTG0_i_ADPPRBENB ((1 << 16) | 439) +#define NX_TIEOFF_USB20OTG0_i_VBUSVLDEXT ((1 << 16) | 440) +#define NX_TIEOFF_USB20OTG0_i_VBUSVLDEXTSEL ((1 << 16) | 441) +#define NX_TIEOFF_USB20OTG0_i_DPPULLDOWN ((1 << 16) | 442) +#define NX_TIEOFF_USB20OTG0_i_DMPULLDOWN ((1 << 16) | 443) +#define NX_TIEOFF_USB20OTG0_i_SUSPENDM_ENB ((1 << 16) | 444) +#define NX_TIEOFF_USB20OTG0_i_SUSPENDM ((1 << 16) | 445) +#define NX_TIEOFF_USB20OTG0_i_SLEEPM_ENB ((1 << 16) | 446) +#define NX_TIEOFF_USB20OTG0_i_SLEEPM ((1 << 16) | 447) +#define NX_TIEOFF_USB20OTG0_i_OPMODE_ENB ((1 << 16) | 448) +#define NX_TIEOFF_USB20OTG0_i_OPMODE ((2 << 16) | 449) +#define NX_TIEOFF_USB20OTG0_i_TERMSEL_ENB ((1 << 16) | 451) +#define NX_TIEOFF_USB20OTG0_i_TERMSEL ((1 << 16) | 452) +#define NX_TIEOFF_USB20OTG0_i_XCVRSEL_ENB ((1 << 16) | 453) +#define NX_TIEOFF_USB20OTG0_i_XCVRSEL ((2 << 16) | 454) +#define NX_TIEOFF_USB20OTG0_i_WORDINTERFACE_ENB ((1 << 16) | 456) +#define NX_TIEOFF_USB20OTG0_i_WORDINTERFACE ((1 << 16) | 457) +#define NX_TIEOFF_USB20OTG0_i_TXBITSTUFFEN ((1 << 16) | 458) +#define NX_TIEOFF_USB20OTG0_i_TXBITSTUFFENH ((1 << 16) | 459) +#define NX_TIEOFF_USB20OTG0_i_BYPASSDPDATA ((1 << 16) | 460) +#define NX_TIEOFF_USB20OTG0_i_BYPASSDMDATA ((1 << 16) | 461) +#define NX_TIEOFF_USB20OTG0_i_BYPASSDPEN ((1 << 16) | 462) +#define NX_TIEOFF_USB20OTG0_i_BYPASSDMEN ((1 << 16) | 463) +#define NX_TIEOFF_USB20OTG0_i_BYPASSSEL ((1 << 16) | 464) +#define NX_TIEOFF_USB20OTG0_i_COMPDISTUNE ((3 << 16) | 465) +#define NX_TIEOFF_USB20OTG0_i_SQRXTUNE ((3 << 16) | 468) +#define NX_TIEOFF_USB20OTG0_i_OTGTUNE ((3 << 16) | 471) +#define NX_TIEOFF_USB20OTG0_i_TXHSXVTUNE ((2 << 16) | 474) +#define NX_TIEOFF_USB20OTG0_i_TXFSLSTUNE ((4 << 16) | 476) +#define NX_TIEOFF_USB20OTG0_i_TXVREFTUNE ((4 << 16) | 480) +#define NX_TIEOFF_USB20OTG0_i_TXRISETUNE ((2 << 16) | 484) +#define NX_TIEOFF_USB20OTG0_i_TXRESTUNE ((2 << 16) | 486) +#define NX_TIEOFF_USB20OTG0_i_TXPREEMPAMPTUNE ((2 << 16) | 488) +#define NX_TIEOFF_USB20OTG0_i_TXPREEMPPULSETUNE ((1 << 16) | 490) +#define NX_TIEOFF_USB20OTG0_i_CHRGSEL ((1 << 16) | 491) +#define NX_TIEOFF_USB20OTG0_i_VDATDETENB ((1 << 16) | 492) +#define NX_TIEOFF_USB20OTG0_i_VDATSRCENB ((1 << 16) | 493) +#define NX_TIEOFF_USB20OTG0_i_DCDENB ((1 << 16) | 494) +#define NX_TIEOFF_USB20OTG0_i_ACAENB ((1 << 16) | 495) +#define NX_TIEOFF_USB20OTG0_i_IDPULLUP_ENB ((1 << 16) | 496) +#define NX_TIEOFF_USB20OTG0_i_DPPULLDOWN_ENB ((1 << 16) | 497) +#define NX_TIEOFF_USB20OTG0_i_DMPULLDOWN_ENB ((1 << 16) | 498) +#define NX_TIEOFF_USB20OTG0_i_DRVVBUS_ENB ((1 << 16) | 499) +#define NX_TIEOFF_USB20OTG0_i_LPMClkMuxCntrl ((1 << 16) | 500) +#define NX_TIEOFF_USB20OTG0_i_GLITCHLESSMUXCntrl ((1 << 16) | 501) +#define NX_TIEOFF_CODA9600_i_nPWRDN00 ((4 << 16) | 502) +#define NX_TIEOFF_CODA9600_i_nSLEEP00 ((4 << 16) | 506) +#define NX_TIEOFF_CODA9600_i_nPWRDN01 ((8 << 16) | 512) +#define NX_TIEOFF_CODA9600_i_nSLEEP01 ((8 << 16) | 520) +#define NX_TIEOFF_CODA9600_i_nPWRDN02 ((10 << 16) | 528) +#define NX_TIEOFF_CODA9600_i_nSLEEP02 ((10 << 16) | 544) +#define NX_TIEOFF_CODA9600_i_nPWRDN03 ((2 << 16) | 554) +#define NX_TIEOFF_CODA9600_i_nSLEEP03 ((2 << 16) | 556) +#define NX_TIEOFF_CODA9600_i_nPWRDN04 ((8 << 16) | 558) +#define NX_TIEOFF_CODA9600_i_nSLEEP04 ((8 << 16) | 566) +#define NX_TIEOFF_CODA9600_i_nPWRDN05 ((3 << 16) | 576) +#define NX_TIEOFF_CODA9600_i_nSLEEP05 ((3 << 16) | 579) +#define NX_TIEOFF_CODA9600_i_nPWRDN06 ((7 << 16) | 582) +#define NX_TIEOFF_CODA9600_i_nSLEEP06 ((7 << 16) | 589) +#define NX_TIEOFF_CODA9600_i_nPWRDN07 ((12 << 16) | 596) +#define NX_TIEOFF_CODA9600_i_nSLEEP07 ((12 << 16) | 608) +#define NX_TIEOFF_CODA9600_i_nPWRDN08 ((1 << 16) | 620) +#define NX_TIEOFF_CODA9600_i_nSLEEP08 ((1 << 16) | 621) +#define NX_TIEOFF_CODA9600_i_nPWRDN09 ((2 << 16) | 622) +#define NX_TIEOFF_CODA9600_i_nSLEEP09 ((2 << 16) | 624) +#define NX_TIEOFF_CODA9600_i_nPWRDN10 ((10 << 16) | 626) +#define NX_TIEOFF_CODA9600_i_nSLEEP10 ((10 << 16) | 640) +#define NX_TIEOFF_CODA9600_i_nPWRDN11 ((1 << 16) | 650) +#define NX_TIEOFF_CODA9600_i_nSLEEP11 ((1 << 16) | 651) +#define NX_TIEOFF_CODA9600_i_TIE_rf2_EMAA ((3 << 16) | 652) +#define NX_TIEOFF_CODA9600_i_TIE_rf2_EMAB ((3 << 16) | 655) +#define NX_TIEOFF_CODA9600_i_TIE_rf2w_EMAA ((3 << 16) | 658) +#define NX_TIEOFF_CODA9600_i_TIE_rf2w_EMAB ((3 << 16) | 661) +#define NX_TIEOFF_CODA9600_i_TIE_ra2_EMAA ((3 << 16) | 664) +#define NX_TIEOFF_CODA9600_i_TIE_ra2_EMAB ((3 << 16) | 667) +#define NX_TIEOFF_CODA9600_i_TIE_ra2_EMAWA ((2 << 16) | 670) +#define NX_TIEOFF_CODA9600_i_TIE_ra2_EMAWB ((2 << 16) | 672) +#define NX_TIEOFF_CODA9600_i_TIE_ra2w_EMAA ((3 << 16) | 674) +#define NX_TIEOFF_CODA9600_i_TIE_ra2w_EMAB ((3 << 16) | 677) +#define NX_TIEOFF_CODA9600_i_TIE_ra2w_EMAWA ((2 << 16) | 680) +#define NX_TIEOFF_CODA9600_i_TIE_ra2w_EMAWB ((2 << 16) | 682) +#define NX_TIEOFF_CODA9600_i_TIE_rf1_EMA ((3 << 16) | 684) +#define NX_TIEOFF_CODA9600_i_TIE_rf1_EMAW ((2 << 16) | 687) +#define NX_TIEOFF_CODA9600_i_TIE_rf1w_EMA ((3 << 16) | 689) +#define NX_TIEOFF_CODA9600_i_TIE_rf1w_EMAW ((2 << 16) | 692) +#define NX_TIEOFF_DWC_GMAC0_sbd_flowctrl_i ((1 << 16) | 694) +#define NX_TIEOFF_DWC_GMAC0_phy_intf_sel_i ((3 << 16) | 695) +#define NX_TIEOFF_DWC_GMAC0_i_NX_RF2_EMAA ((3 << 16) | 698) +#define NX_TIEOFF_DWC_GMAC0_i_NX_RF2_EMAB ((3 << 16) | 701) +#define NX_TIEOFF_MALI4000_NX_DPSRAM_1R1W_EMAA ((3 << 16) | 704) +#define NX_TIEOFF_MALI4000_NX_DPSRAM_1R1W_EMAB ((3 << 16) | 707) +#define NX_TIEOFF_MALI4000_NX_SPSRAM_EMA ((3 << 16) | 710) +#define NX_TIEOFF_MALI4000_NX_SPSRAM_EMAW ((2 << 16) | 713) +#define NX_TIEOFF_MALI4000_NX_SPSRAM_BW_EMA ((3 << 16) | 715) +#define NX_TIEOFF_MALI4000_NX_SPSRAM_BW_EMAW ((2 << 16) | 718) +#define NX_TIEOFF_MALI4000_PWRDNBYPASS ((1 << 16) | 720) +#define NX_TIEOFF_MALI4000_GP_NX_NPOWERDOWN ((15 << 16) | 721) +#define NX_TIEOFF_MALI4000_GP_NX_NSLEEP ((15 << 16) | 736) +#define NX_TIEOFF_MALI4000_L2_NX_NPOWERDOWN ((3 << 16) | 751) +#define NX_TIEOFF_MALI4000_L2_NX_NSLEEP ((3 << 16) | 754) +#define NX_TIEOFF_MALI4000_PP0_NX_NPOWERDOWN ((32 << 16) | 768) +#define NX_TIEOFF_MALI4000_PP0_NX_NSLEEP ((32 << 16) | 800) +#define NX_TIEOFF_MALI4000_PP1_NX_NPOWERDOWN ((32 << 16) | 832) +#define NX_TIEOFF_MALI4000_PP1_NX_NSLEEP ((32 << 16) | 864) +#define NX_TIEOFF_MALI4000_PP2_NX_NPOWERDOWN ((32 << 16) | 896) +#define NX_TIEOFF_MALI4000_PP2_NX_NSLEEP ((32 << 16) | 928) +#define NX_TIEOFF_MALI4000_PP3_NX_NPOWERDOWN ((32 << 16) | 960) +#define NX_TIEOFF_MALI4000_PP3_NX_NSLEEP ((32 << 16) | 992) +#define NX_TIEOFF_A3BM_AXI_PERI_BUS0_SYNCMODEREQm9 ((1 << 16) | 1024) +#define NX_TIEOFF_A3BM_AXI_PERI_BUS0_SYNCMODEREQm10 ((1 << 16) | 1025) +#define NX_TIEOFF_A3BM_AXI_PERI_BUS0_SYNCMODEREQm16 ((1 << 16) | 1026) +#define NX_TIEOFF_A3BM_AXI_TOP_MASTER_BUS0_REMAP ((2 << 16) | 1027) +#if defined(CONFIG_ARCH_S5P6818) +#define NX_TIEOFF_Inst_ARMTOP_SMPEN ((4 << 16) | 2816) +#define NX_TIEOFF_Inst_ARMTOP_STANBYWFI ((4 << 16) | 2880) +#define NX_TIEOFF_Inst_ARMTOP_STANBYWFIL2 ((1 << 16) | 2884) +#define NX_TIEOFF_Inst_ARMTOP_DBGNOPWRDWN ((4 << 16) | 2889) +#define NX_TIEOFF_Inst_ARMTOP_DBGPWRUPREQ ((4 << 16) | 2893) +#define NX_TIEOFF_Inst_ARMTOP_COREPWRDOWNPRE ((1 << 16) | 2901) +#define NX_TIEOFF_Inst_ARMTOP_CPU0PWRDOWNPRE ((1 << 16) | 2902) +#define NX_TIEOFF_Inst_ARMTOP_CPU1PWRDOWNPRE ((1 << 16) | 2903) +#define NX_TIEOFF_Inst_ARMTOP_CPU2PWRDOWNPRE ((1 << 16) | 2904) +#define NX_TIEOFF_Inst_ARMTOP_CPU3PWRDOWNPRE ((1 << 16) | 2905) +#define NX_TIEOFF_Inst_ARMTOP_COREPWRDOWNALL ((1 << 16) | 2906) +#define NX_TIEOFF_Inst_ARMTOP_CPU0PWRDOWNALL ((1 << 16) | 2907) +#define NX_TIEOFF_Inst_ARMTOP_CPU1PWRDOWNALL ((1 << 16) | 2908) +#define NX_TIEOFF_Inst_ARMTOP_CPU2PWRDOWNALL ((1 << 16) | 2909) +#define NX_TIEOFF_Inst_ARMTOP_CPU3PWRDOWNALL ((1 << 16) | 2910) +#define NX_TIEOFF_Inst_ARMTOP_CLAMPL2 ((1 << 16) | 2920) +#define NX_TIEOFF_Inst_ARMTOP_L2FLUSHREQ ((1 << 16) | 3018) +#define NX_TIEOFF_Inst_ARMTOP_L2FLUSHDONE ((1 << 16) | 3019) +#define NX_TIEOFF_Inst_ARMTOP_ACINACTM ((1 << 16) | 3023) +#define NX_TIEOFF_Inst_ARMTOP_P1_SMPEN ((4 << 16) | 3360) +#define NX_TIEOFF_Inst_ARMTOP_P1_STANBYWFI ((4 << 16) | 3424) +#define NX_TIEOFF_Inst_ARMTOP_P1_STANBYWFIL2 ((1 << 16) | 3428) +#define NX_TIEOFF_Inst_ARMTOP_P1_DBGNOPWRDWN ((4 << 16) | 3442) +#define NX_TIEOFF_Inst_ARMTOP_P1_DBGPWRUPREQ ((4 << 16) | 3443) +#define NX_TIEOFF_Inst_ARMTOP_P1_DBGPWRDUP ((4 << 16) | 3444) +#define NX_TIEOFF_Inst_ARMTOP_P1_COREPWRDOWNPRE ((1 << 16) | 3445) +#define NX_TIEOFF_Inst_ARMTOP_P1_CPU0PWRDOWNPRE ((1 << 16) | 3446) +#define NX_TIEOFF_Inst_ARMTOP_P1_CPU1PWRDOWNPRE ((1 << 16) | 3447) +#define NX_TIEOFF_Inst_ARMTOP_P1_CPU2PWRDOWNPRE ((1 << 16) | 3448) +#define NX_TIEOFF_Inst_ARMTOP_P1_CPU3PWRDOWNPRE ((1 << 16) | 3449) +#define NX_TIEOFF_Inst_ARMTOP_P1_COREPWRDOWNALL ((1 << 16) | 3450) +#define NX_TIEOFF_Inst_ARMTOP_P1_CPU0PWRDOWNALL ((1 << 16) | 3451) +#define NX_TIEOFF_Inst_ARMTOP_P1_CPU1PWRDOWNALL ((1 << 16) | 3452) +#define NX_TIEOFF_Inst_ARMTOP_P1_CPU2PWRDOWNALL ((1 << 16) | 3453) +#define NX_TIEOFF_Inst_ARMTOP_P1_CPU3PWRDOWNALL ((1 << 16) | 3454) +#define NX_TIEOFF_Inst_ARMTOP_P1_CLAMPL2 ((1 << 16) | 3464) +#define NX_TIEOFF_Inst_ARMTOP_P1_L2FLUSHREQ ((1 << 16) | 3562) +#define NX_TIEOFF_Inst_ARMTOP_P1_L2FLUSHDONE ((1 << 16) | 3563) +#define NX_TIEOFF_Inst_ARMTOP_P1_ACINACTM ((1 << 16) | 3567) +#endif + +#endif /* _NEXELL_TIEOFF_H */

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01: - SPL not supported yet --> no spl-directory in arch/arm/mach-nexell. Appropriate line in Makefile removed. - clock.c: 'section(".data")' added to declaration of clk_periphs[] and core_hz. - Kconfig: Changes to have a structure like in mach-bcm283x/Kconfig, e.g. "config ..." entries moved from other Kconfig. - timer.c: 'section(".data")' added to declaration of timestamp and lastdec.
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
arch/arm/Kconfig | 7 + arch/arm/Makefile | 1 + arch/arm/mach-nexell/Kconfig | 67 +++ arch/arm/mach-nexell/Makefile | 15 + arch/arm/mach-nexell/clock.c | 869 ++++++++++++++++++++++++++++++++++ arch/arm/mach-nexell/cmd_boot_linux.c | 145 ++++++ arch/arm/mach-nexell/config.mk | 11 + arch/arm/mach-nexell/nx_gpio.c | 352 ++++++++++++++ arch/arm/mach-nexell/nx_sec_reg.c | 82 ++++ arch/arm/mach-nexell/reg-call.S | 23 + arch/arm/mach-nexell/reset.c | 33 ++ arch/arm/mach-nexell/serial.c | 262 ++++++++++ arch/arm/mach-nexell/tieoff.c | 109 +++++ arch/arm/mach-nexell/timer.c | 297 ++++++++++++ 14 files changed, 2273 insertions(+) create mode 100644 arch/arm/mach-nexell/Kconfig create mode 100644 arch/arm/mach-nexell/Makefile create mode 100644 arch/arm/mach-nexell/clock.c create mode 100644 arch/arm/mach-nexell/cmd_boot_linux.c create mode 100644 arch/arm/mach-nexell/config.mk create mode 100644 arch/arm/mach-nexell/nx_gpio.c create mode 100644 arch/arm/mach-nexell/nx_sec_reg.c create mode 100644 arch/arm/mach-nexell/reg-call.S create mode 100644 arch/arm/mach-nexell/reset.c create mode 100644 arch/arm/mach-nexell/serial.c create mode 100644 arch/arm/mach-nexell/tieoff.c create mode 100644 arch/arm/mach-nexell/timer.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index a623ef5..469aa65 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -875,6 +875,11 @@ config ARCH_MX5 select CPU_V7A imply MXC_GPIO
+config ARCH_NEXELL + bool "Nexell S5P4418/S5P6818 SoC" + select ENABLE_ARM_SOC_BOOT0_HOOK + select DM + config ARCH_OWL bool "Actions Semi OWL SoCs" select ARM64 @@ -1797,6 +1802,8 @@ source "arch/arm/cpu/armv8/Kconfig"
source "arch/arm/mach-imx/Kconfig"
+source "arch/arm/mach-nexell/Kconfig" + source "board/bosch/shc/Kconfig" source "board/bosch/guardian/Kconfig" source "board/CarMediaLab/flea3/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 1e60a9f..5a390b8 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -67,6 +67,7 @@ machine-$(CONFIG_ARCH_MESON) += meson machine-$(CONFIG_ARCH_MVEBU) += mvebu # TODO: rename CONFIG_TEGRA -> CONFIG_ARCH_TEGRA # TODO: rename CONFIG_ORION5X -> CONFIG_ARCH_ORION5X +machine-$(CONFIG_ARCH_NEXELL) += nexell machine-$(CONFIG_ORION5X) += orion5x machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2 machine-$(CONFIG_ARCH_OWL) += owl diff --git a/arch/arm/mach-nexell/Kconfig b/arch/arm/mach-nexell/Kconfig new file mode 100644 index 0000000..5368909 --- /dev/null +++ b/arch/arm/mach-nexell/Kconfig @@ -0,0 +1,67 @@ +if ARCH_NEXELL + +config NEXELL_COMMON + bool "Nexell common options" + +config NEXELL_ARMV7_COMMON + bool "Nexell 32-bit common options" + select CPU_V7A + select NEXELL_COMMON + #select SUPPORT_SPL + +config NEXELL_ARMV8_COMMON + bool "Nexell 64-bit common options" + select ARM64 + select ARMV8_MULTIENTRY + select NEXELL_COMMON + +config ARCH_S5P4418 + bool "Nexell S5P4418 SoC" + select NEXELL_ARMV7_COMMON + select OF_CONTROL + select OF_SEPARATE + select NX_GPIO + select PL011_SERIAL + select PL011_SERIAL_FLUSH_ON_INIT + +config ARCH_S5P6818 + bool "Nexell S5P6818 SoC" + select NEXELL_ARMV8_COMMON + +menu "Nexell S5P4418/S5P6818" + #depends on ARCH_S5P4418 + depends on ARCH_NEXELL + +choice + prompt "Nexell S5P4418/S5P6818 board select" + optional + +config TARGET_NANOPI2 + bool "FriendlyARM NanoPi2 Board" + select ARCH_S5P4418 + +endchoice + +config SYS_BOARD + default "nanopi2" + +config SYS_VENDOR + default "friendlyarm" + +config SYS_SOC + default "nexell" + +config SYS_CONFIG_NAME + default "s5p4418_nanopi2" + +endmenu + +config SYS_PLLFIN + int + +config TIMER_SYS_TICK_CH + int + +source "board/friendlyarm/Kconfig" + +endif diff --git a/arch/arm/mach-nexell/Makefile b/arch/arm/mach-nexell/Makefile new file mode 100644 index 0000000..e144196 --- /dev/null +++ b/arch/arm/mach-nexell/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2016 Nexell +# Hyunseok, Jung hsjung@nexell.co.kr + +obj-y += clock.o +obj-y += timer.o +obj-y += reset.o +obj-y += nx_gpio.o +obj-y += tieoff.o +obj-$(CONFIG_ARCH_S5P4418) += reg-call.o +obj-$(CONFIG_ARCH_S5P4418) += nx_sec_reg.o +obj-$(CONFIG_CMD_BOOTL) += cmd_boot_linux.o +obj-$(CONFIG_S5P_SERIAL) += serial.o +obj-$(CONFIG_SPL_BUILD) += spl/ diff --git a/arch/arm/mach-nexell/clock.c b/arch/arm/mach-nexell/clock.c new file mode 100644 index 0000000..a0ba2d8 --- /dev/null +++ b/arch/arm/mach-nexell/clock.c @@ -0,0 +1,869 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Hyunseok, Jung hsjung@nexell.co.kr + */ + +#include <common.h> +#include <command.h> +#include <linux/err.h> +#include <asm/io.h> +#include <asm/arch/nexell.h> +#include <asm/arch/clk.h> + +/* + * clock generator macros + */ +#define I_PLL0_BIT (0) +#define I_PLL1_BIT (1) +#define I_PLL2_BIT (2) +#define I_PLL3_BIT (3) +#define I_EXT1_BIT (4) +#define I_EXT2_BIT (5) +#define I_CLKn_BIT (7) +#define I_EXT1_BIT_FORCE (8) +#define I_EXT2_BIT_FORCE (9) + +#define I_CLOCK_NUM 6 /* PLL0, PLL1, PLL2, PLL3, EXT1, EXT2 */ + +#define I_EXECEPT_CLK (0) +#define I_CLOCK_MASK (((1 << I_CLOCK_NUM) - 1) & ~I_EXECEPT_CLK) + +#define I_PLL0 (1 << I_PLL0_BIT) +#define I_PLL1 (1 << I_PLL1_BIT) +#define I_PLL2 (1 << I_PLL2_BIT) +#define I_PLL3 (1 << I_PLL3_BIT) +#define I_EXTCLK1 (1 << I_EXT1_BIT) +#define I_EXTCLK2 (1 << I_EXT2_BIT) +#define I_EXTCLK1_FORCE (1 << I_EXT1_BIT_FORCE) +#define I_EXTCLK2_FORCE (1 << I_EXT2_BIT_FORCE) + +#define I_PLL_0_1 (I_PLL0 | I_PLL1) +#define I_PLL_0_2 (I_PLL_0_1 | I_PLL2) +#define I_PLL_0_3 (I_PLL_0_2 | I_PLL3) +#define I_CLKnOUT (0) + +#define I_PCLK (1 << 16) +#define I_BCLK (1 << 17) +#define I_GATE_PCLK (1 << 20) +#define I_GATE_BCLK (1 << 21) +#define I_PCLK_MASK (I_GATE_PCLK | I_PCLK) +#define I_BCLK_MASK (I_GATE_BCLK | I_BCLK) + +struct clk_dev_peri { + const char *dev_name; + void __iomem *base; + int dev_id; + int periph_id; + int clk_step; + u32 in_mask; + u32 in_mask1; + int div_src_0; + int div_val_0; + int invert_0; + int div_src_1; + int div_val_1; + int invert_1; + int in_extclk_1; + int in_extclk_2; +}; + +struct clk_dev { + struct clk clk; + struct clk *link; + const char *name; + struct clk_dev_peri *peri; +}; + +struct clk_dev_map { + unsigned int con_enb; + unsigned int con_gen[4]; +}; + +#define CLK_PERI_1S(name, devid, id, addr, mk)[id] = \ + { .dev_name = name, .dev_id = devid, .periph_id = id, .clk_step = 1, \ + .base = (void *)addr, .in_mask = mk, } + +#define CLK_PERI_2S(name, devid, id, addr, mk, mk2)[id] = \ + { .dev_name = name, .dev_id = devid, .periph_id = id, .clk_step = 2, \ + .base = (void *)addr, .in_mask = mk, .in_mask1 = mk2, } + +static const char * const clk_core[] = { + CORECLK_NAME_PLL0, CORECLK_NAME_PLL1, CORECLK_NAME_PLL2, + CORECLK_NAME_PLL3, CORECLK_NAME_FCLK, CORECLK_NAME_MCLK, + CORECLK_NAME_BCLK, CORECLK_NAME_PCLK, CORECLK_NAME_HCLK, +}; + +/* + * Section ".data" must be used because BSS is not available before relocation, + * in board_init_f(), respectively! I.e. global variables can not be used! + */ +static struct clk_dev_peri clk_periphs[] + __attribute__((section(".data"))) = { + CLK_PERI_1S(DEV_NAME_TIMER, 0, CLK_ID_TIMER_0, + PHY_BASEADDR_CLKGEN14, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_TIMER, 1, CLK_ID_TIMER_1, + PHY_BASEADDR_CLKGEN0, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_TIMER, 2, CLK_ID_TIMER_2, + PHY_BASEADDR_CLKGEN1, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_TIMER, 3, CLK_ID_TIMER_3, + PHY_BASEADDR_CLKGEN2, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 0, CLK_ID_UART_0, + PHY_BASEADDR_CLKGEN22, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 1, CLK_ID_UART_1, + PHY_BASEADDR_CLKGEN24, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 2, CLK_ID_UART_2, + PHY_BASEADDR_CLKGEN23, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 3, CLK_ID_UART_3, + PHY_BASEADDR_CLKGEN25, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 4, CLK_ID_UART_4, + PHY_BASEADDR_CLKGEN26, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_UART, 5, CLK_ID_UART_5, + PHY_BASEADDR_CLKGEN27, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_PWM, 0, CLK_ID_PWM_0, + PHY_BASEADDR_CLKGEN13, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_PWM, 1, CLK_ID_PWM_1, + PHY_BASEADDR_CLKGEN3, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_PWM, 2, CLK_ID_PWM_2, + PHY_BASEADDR_CLKGEN4, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_PWM, 3, CLK_ID_PWM_3, + PHY_BASEADDR_CLKGEN5, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_I2C, 0, CLK_ID_I2C_0, + PHY_BASEADDR_CLKGEN6, (I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_I2C, 1, CLK_ID_I2C_1, + PHY_BASEADDR_CLKGEN7, (I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_I2C, 2, CLK_ID_I2C_2, + PHY_BASEADDR_CLKGEN8, (I_GATE_PCLK)), + CLK_PERI_2S(DEV_NAME_GMAC, 0, CLK_ID_GMAC, + PHY_BASEADDR_CLKGEN10, + (I_PLL_0_3 | I_EXTCLK1 | I_EXTCLK1_FORCE), + (I_CLKnOUT)), + CLK_PERI_2S(DEV_NAME_I2S, 0, CLK_ID_I2S_0, + PHY_BASEADDR_CLKGEN15, (I_PLL_0_3 | I_EXTCLK1), + (I_CLKnOUT)), + CLK_PERI_2S(DEV_NAME_I2S, 1, CLK_ID_I2S_1, + PHY_BASEADDR_CLKGEN16, (I_PLL_0_3 | I_EXTCLK1), + (I_CLKnOUT)), + CLK_PERI_2S(DEV_NAME_I2S, 2, CLK_ID_I2S_2, + PHY_BASEADDR_CLKGEN17, (I_PLL_0_3 | I_EXTCLK1), + (I_CLKnOUT)), + CLK_PERI_1S(DEV_NAME_SDHC, 0, CLK_ID_SDHC_0, + PHY_BASEADDR_CLKGEN18, (I_PLL_0_2 | I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_SDHC, 1, CLK_ID_SDHC_1, + PHY_BASEADDR_CLKGEN19, (I_PLL_0_2 | I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_SDHC, 2, CLK_ID_SDHC_2, + PHY_BASEADDR_CLKGEN20, (I_PLL_0_2 | I_GATE_PCLK)), + CLK_PERI_1S(DEV_NAME_SPI, 0, CLK_ID_SPI_0, + PHY_BASEADDR_CLKGEN37, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_SPI, 1, CLK_ID_SPI_1, + PHY_BASEADDR_CLKGEN38, (I_PLL_0_2)), + CLK_PERI_1S(DEV_NAME_SPI, 2, CLK_ID_SPI_2, + PHY_BASEADDR_CLKGEN39, (I_PLL_0_2)), +}; + +#define CLK_PERI_NUM ((int)ARRAY_SIZE(clk_periphs)) +#define CLK_CORE_NUM ((int)ARRAY_SIZE(clk_core)) +#define CLK_DEVS_NUM (CLK_CORE_NUM + CLK_PERI_NUM) +#define MAX_DIVIDER ((1 << 8) - 1) /* 256, align 2 */ + +static struct clk_dev st_clk_devs[CLK_DEVS_NUM] + __attribute__((section(".data"))); +#define clk_dev_get(n) ((struct clk_dev *)&st_clk_devs[n]) +#define clk_container(p) (container_of(p, struct clk_dev, clk)) + +/* + * Core frequencys + */ +struct _core_hz_ { + unsigned long pll[4]; /* PLL */ + unsigned long cpu_fclk, cpu_bclk; /* cpu */ + unsigned long mem_fclk, mem_dclk, mem_bclk, mem_pclk; /* ddr */ + unsigned long bus_bclk, bus_pclk; /* bus */ +#if defined(CONFIG_ARCH_S5P6818) + unsigned long cci4_bclk, cci4_pclk; /* cci */ +#endif + /* ip */ + unsigned long g3d_bclk; + unsigned long coda_bclk, coda_pclk; +#if defined(CONFIG_ARCH_S5P6818) + unsigned long disp_bclk, disp_pclk; + unsigned long hdmi_pclk; +#endif +}; + +/* + * Section ".data" must be used because BSS is not available before relocation, + * in board_init_f(), respectively! I.e. global variables can not be used! + */ +/* core clock */ +static struct _core_hz_ core_hz __attribute__((section(".data"))); + +#define CORE_HZ_SIZE (sizeof(core_hz) / 4) + +/* + * CLKGEN HW + */ +static inline void clk_dev_bclk(void *base, int on) +{ + struct clk_dev_map *reg = base; + unsigned int val = readl(®->con_enb) & ~(0x3); + + val |= (on ? 3 : 0) & 0x3; /* always BCLK */ + writel(val, ®->con_enb); +} + +static inline void clk_dev_pclk(void *base, int on) +{ + struct clk_dev_map *reg = base; + unsigned int val = 0; + + if (!on) + return; + + val = readl(®->con_enb) & ~(1 << 3); + val |= (1 << 3); + writel(val, ®->con_enb); +} + +static inline void clk_dev_rate(void *base, int step, int src, int div) +{ + struct clk_dev_map *reg = base; + unsigned int val = 0; + + val = readl(®->con_gen[step << 1]); + val &= ~(0x07 << 2); + val |= (src << 2); /* source */ + val &= ~(0xFF << 5); + val |= (div - 1) << 5; /* divider */ + writel(val, ®->con_gen[step << 1]); +} + +static inline void clk_dev_inv(void *base, int step, int inv) +{ + struct clk_dev_map *reg = base; + unsigned int val = readl(®->con_gen[step << 1]) & ~(1 << 1); + + val |= (inv << 1); + writel(val, ®->con_gen[step << 1]); +} + +static inline void clk_dev_enb(void *base, int on) +{ + struct clk_dev_map *reg = base; + unsigned int val = readl(®->con_enb) & ~(1 << 2); + + val |= ((on ? 1 : 0) << 2); + writel(val, ®->con_enb); +} + +/* + * CORE FREQUENCY + * + * PLL0 [P,M,S] ------- | | ----- [DIV0] --- CPU-G0 + * |M| ----- [DIV1] --- BCLK/PCLK + * PLL1 [P,M,S] ------- | | ----- [DIV2] --- DDR + * |U| ----- [DIV3] --- 3D + * PLL2 [P,M,S,K]-------| | ----- [DIV4] --- CODA + * |X| ----- [DIV5] --- DISPLAY + * PLL3 [P,M,S,K]-------| | ----- [DIV6] --- HDMI + * | | ----- [DIV7] --- CPU-G1 + * | | ----- [DIV8] --- CCI-400(FASTBUS) + * + */ + +struct nx_clkpwr_registerset { + u32 clkmodereg0; /* 0x000 : Clock Mode Register0 */ + u32 __reserved0; /* 0x004 */ + u32 pllsetreg[4]; /* 0x008 ~ 0x014 : PLL Setting Register */ + u32 __reserved1[2]; /* 0x018 ~ 0x01C */ + u32 dvoreg[9]; /* 0x020 ~ 0x040 : Divider Setting Register */ + u32 __Reserved2; /* 0x044 */ + u32 pllsetreg_sscg[6]; /* 0x048 ~ 0x05C */ + u32 __reserved3[8]; /* 0x060 ~ 0x07C */ + u8 __reserved4[0x200 - 0x80]; /* padding (0x80 ~ 0x1FF) */ + u32 gpiowakeupriseenb; /* 0x200 : GPIO Rising Edge Detect En. Reg. */ + u32 gpiowakeupfallenb; /* 0x204 : GPIO Falling Edge Detect En. Reg. */ + u32 gpiorstenb; /* 0x208 : GPIO Reset Enable Register */ + u32 gpiowakeupenb; /* 0x20C : GPIO Wakeup Source Enable */ + u32 gpiointenb; /* 0x210 : Interrupt Enable Register */ + u32 gpiointpend; /* 0x214 : Interrupt Pend Register */ + u32 resetstatus; /* 0x218 : Reset Status Register */ + u32 intenable; /* 0x21C : Interrupt Enable Register */ + u32 intpend; /* 0x220 : Interrupt Pend Register */ + u32 pwrcont; /* 0x224 : Power Control Register */ + u32 pwrmode; /* 0x228 : Power Mode Register */ + u32 __reserved5; /* 0x22C : Reserved Region */ + u32 scratch[3]; /* 0x230 ~ 0x238 : Scratch Register */ + u32 sysrstconfig; /* 0x23C : System Reset Configuration Reg. */ + u8 __reserved6[0x2A0 - 0x240]; /* padding (0x240 ~ 0x29F) */ + u32 cpupowerdownreq; /* 0x2A0 : CPU Power Down Request Register */ + u32 cpupoweronreq; /* 0x2A4 : CPU Power On Request Register */ + u32 cpuresetmode; /* 0x2A8 : CPU Reset Mode Register */ + u32 cpuwarmresetreq; /* 0x2AC : CPU Warm Reset Request Register */ + u32 __reserved7; /* 0x2B0 */ + u32 cpustatus; /* 0x2B4 : CPU Status Register */ + u8 __reserved8[0x400 - 0x2B8]; /* padding (0x2B8 ~ 0x33F) */ +}; + +static struct nx_clkpwr_registerset * const clkpwr = + (struct nx_clkpwr_registerset *)PHY_BASEADDR_CLKPWR; + +#define getquotient(v, d) ((v) / (d)) + +#define DIV_CPUG0 0 +#define DIV_BUS 1 +#define DIV_MEM 2 +#define DIV_G3D 3 +#define DIV_CODA 4 +#if defined(CONFIG_ARCH_S5P6818) +#define DIV_DISP 5 +#define DIV_HDMI 6 +#define DIV_CPUG1 7 +#define DIV_CCI4 8 +#endif + +#define DVO0 3 +#define DVO1 9 +#define DVO2 15 +#define DVO3 21 + +static unsigned int pll_rate(unsigned int plln, unsigned int xtal) +{ + unsigned int val, val1, nP, nM, nS, nK; + unsigned int temp = 0; + + val = clkpwr->pllsetreg[plln]; + val1 = clkpwr->pllsetreg_sscg[plln]; + xtal /= 1000; /* Unit Khz */ + + nP = (val >> 18) & 0x03F; + nM = (val >> 8) & 0x3FF; + nS = (val >> 0) & 0x0FF; + nK = (val1 >> 16) & 0xFFFF; + + if (plln > 1 && nK) { + temp = (unsigned int)(getquotient((getquotient((nK * 1000), + 65536) * xtal), nP) >> nS); + } + + temp = (unsigned int)((getquotient((nM * xtal), nP) >> nS) * 1000) + + temp; + return temp; +} + +static unsigned int pll_dvo(int dvo) +{ + unsigned int val; + + val = (clkpwr->dvoreg[dvo] & 0x7); + return val; +} + +static unsigned int pll_div(int dvo) +{ + unsigned int val = clkpwr->dvoreg[dvo]; + + return ((((val >> DVO3) & 0x3F) + 1) << 24) | + ((((val >> DVO2) & 0x3F) + 1) << 16) | + ((((val >> DVO1) & 0x3F) + 1) << 8) | + ((((val >> DVO0) & 0x3F) + 1) << 0); +} + +#define PLLN_RATE(n) (pll_rate(n, CONFIG_SYS_PLLFIN)) /* 0~ 3 */ +#define CPU_FCLK_RATE(n) (pll_rate(pll_dvo(n), CONFIG_SYS_PLLFIN) / \ + ((pll_div(n) >> 0) & 0x3F)) +#define CPU_BCLK_RATE(n) (pll_rate(pll_dvo(n), CONFIG_SYS_PLLFIN) / \ + ((pll_div(n) >> 0) & 0x3F) / \ + ((pll_div(n) >> 8) & 0x3F)) + +#define MEM_FCLK_RATE() (pll_rate(pll_dvo(DIV_MEM), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_MEM) >> 0) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 8) & 0x3F)) + +#define MEM_DCLK_RATE() (pll_rate(pll_dvo(DIV_MEM), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_MEM) >> 0) & 0x3F)) + +#define MEM_BCLK_RATE() (pll_rate(pll_dvo(DIV_MEM), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_MEM) >> 0) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 8) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 16) & 0x3F)) +#define MEM_PCLK_RATE() (pll_rate(pll_dvo(DIV_MEM), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_MEM) >> 0) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 8) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 16) & 0x3F) / \ + ((pll_div(DIV_MEM) >> 24) & 0x3F)) + +#define BUS_BCLK_RATE() (pll_rate(pll_dvo(DIV_BUS), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_BUS) >> 0) & 0x3F)) +#define BUS_PCLK_RATE() (pll_rate(pll_dvo(DIV_BUS), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_BUS) >> 0) & 0x3F) / \ + ((pll_div(DIV_BUS) >> 8) & 0x3F)) + +#define G3D_BCLK_RATE() (pll_rate(pll_dvo(DIV_G3D), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_G3D) >> 0) & 0x3F)) + +#define MPG_BCLK_RATE() (pll_rate(pll_dvo(DIV_CODA), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_CODA) >> 0) & 0x3F)) +#define MPG_PCLK_RATE() (pll_rate(pll_dvo(DIV_CODA), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_CODA) >> 0) & 0x3F) / \ + ((pll_div(DIV_CODA) >> 8) & 0x3F)) + +#if defined(CONFIG_ARCH_S5P6818) +#define DISP_BCLK_RATE() (pll_rate(pll_dvo(DIV_DISP), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_DISP) >> 0) & 0x3F)) +#define DISP_PCLK_RATE() (pll_rate(pll_dvo(DIV_DISP), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_DISP) >> 0) & 0x3F) / \ + ((pll_div(DIV_DISP) >> 8) & 0x3F)) + +#define HDMI_PCLK_RATE() (pll_rate(pll_dvo(DIV_HDMI), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_HDMI) >> 0) & 0x3F)) + +#define CCI4_BCLK_RATE() (pll_rate(pll_dvo(DIV_CCI4), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_CCI4) >> 0) & 0x3F)) +#define CCI4_PCLK_RATE() (pll_rate(pll_dvo(DIV_CCI4), CONFIG_SYS_PLLFIN) / \ + ((pll_div(DIV_CCI4) >> 0) & 0x3F) / \ + ((pll_div(DIV_CCI4) >> 8) & 0x3F)) +#endif + +static void core_update_rate(int type) +{ + switch (type) { + case 0: + core_hz.pll[0] = PLLN_RATE(0); break; + case 1: + core_hz.pll[1] = PLLN_RATE(1); break; + case 2: + core_hz.pll[2] = PLLN_RATE(2); break; + case 3: + core_hz.pll[3] = PLLN_RATE(3); break; + case 4: + core_hz.cpu_fclk = CPU_FCLK_RATE(DIV_CPUG0); break; + case 5: + core_hz.mem_fclk = MEM_FCLK_RATE(); break; + case 6: + core_hz.bus_bclk = BUS_BCLK_RATE(); break; + case 7: + core_hz.bus_pclk = BUS_PCLK_RATE(); break; + case 8: + core_hz.cpu_bclk = CPU_BCLK_RATE(DIV_CPUG0); break; + case 9: + core_hz.mem_dclk = MEM_DCLK_RATE(); break; + case 10: + core_hz.mem_bclk = MEM_BCLK_RATE(); break; + case 11: + core_hz.mem_pclk = MEM_PCLK_RATE(); break; + case 12: + core_hz.g3d_bclk = G3D_BCLK_RATE(); break; + case 13: + core_hz.coda_bclk = MPG_BCLK_RATE(); break; + case 14: + core_hz.coda_pclk = MPG_PCLK_RATE(); break; +#if defined(CONFIG_ARCH_S5P6818) + case 15: + core_hz.disp_bclk = DISP_BCLK_RATE(); break; + case 16: + core_hz.disp_pclk = DISP_PCLK_RATE(); break; + case 17: + core_hz.hdmi_pclk = HDMI_PCLK_RATE(); break; + case 18: + core_hz.cci4_bclk = CCI4_BCLK_RATE(); break; + case 19: + core_hz.cci4_pclk = CCI4_PCLK_RATE(); break; +#endif + }; +} + +static unsigned long core_get_rate(int type) +{ + unsigned long rate = 0; + + switch (type) { + case 0: + rate = core_hz.pll[0]; break; + case 1: + rate = core_hz.pll[1]; break; + case 2: + rate = core_hz.pll[2]; break; + case 3: + rate = core_hz.pll[3]; break; + case 4: + rate = core_hz.cpu_fclk; break; + case 5: + rate = core_hz.mem_fclk; break; + case 6: + rate = core_hz.bus_bclk; break; + case 7: + rate = core_hz.bus_pclk; break; + case 8: + rate = core_hz.cpu_bclk; break; + case 9: + rate = core_hz.mem_dclk; break; + case 10: + rate = core_hz.mem_bclk; break; + case 11: + rate = core_hz.mem_pclk; break; + case 12: + rate = core_hz.g3d_bclk; break; + case 13: + rate = core_hz.coda_bclk; break; + case 14: + rate = core_hz.coda_pclk; break; +#if defined(CONFIG_ARCH_S5P6818) + case 15: + rate = core_hz.disp_bclk; break; + case 16: + rate = core_hz.disp_pclk; break; + case 17: + rate = core_hz.hdmi_pclk; break; + case 18: + rate = core_hz.cci4_bclk; break; + case 19: + rate = core_hz.cci4_pclk; break; +#endif + default: + printf("unknown core clock type %d ...\n", type); + break; + }; + return rate; +} + +static long core_set_rate(struct clk *clk, long rate) +{ + return clk->rate; +} + +static void core_rate_init(void) +{ + int i; + + for (i = 0; i < CORE_HZ_SIZE; i++) + core_update_rate(i); +} + +/* + * Clock Interfaces + */ +static inline long clk_divide(long rate, long request, + int align, int *divide) +{ + int div = (rate / request); + int max = MAX_DIVIDER & ~(align - 1); + int adv = (div & ~(align - 1)) + align; + long ret; + + if (!div) { + if (divide) + *divide = 1; + return rate; + } + + if (div != 1) + div &= ~(align - 1); + + if (div != adv && abs(request - rate / div) > abs(request - rate / adv)) + div = adv; + + div = (div > max ? max : div); + if (divide) + *divide = div; + + ret = rate / div; + return ret; +} + +void clk_put(struct clk *clk) +{ +} + +struct clk *clk_get(const char *id) +{ + struct clk_dev *cdev = clk_dev_get(0); + struct clk *clk = NULL; + const char *str = NULL, *c = NULL; + int i, devid; + + if (id) + str = id; + + for (i = 0; i < CLK_DEVS_NUM; i++, cdev++) { + if (!cdev->name) + continue; + if (!strncmp(cdev->name, str, strlen(cdev->name))) { + c = strrchr((const char *)str, (int)'.'); + if (!c || !cdev->peri) + break; + devid = simple_strtoul(++c, NULL, 10); + if (cdev->peri->dev_id == devid) + break; + } + } + if (i < CLK_DEVS_NUM) + clk = &cdev->clk; + else + clk = &(clk_dev_get(7))->clk; /* pclk */ + + return clk ? clk : ERR_PTR(-ENOENT); +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + struct clk_dev *pll = NULL, *cdev = clk_container(clk); + struct clk_dev_peri *peri = cdev->peri; + unsigned long request = rate, rate_hz = 0; + unsigned int mask; + int step, div[2] = { 0, }; + int i, n, clk2 = 0; + int start_src = 0, max_src = I_CLOCK_NUM; + short s1 = 0, s2 = 0, d1 = 0, d2 = 0; + + if (!peri) + return core_set_rate(clk, rate); + + step = peri->clk_step; + mask = peri->in_mask; + debug("clk: %s.%d request = %ld [input=0x%x]\n", peri->dev_name, + peri->dev_id, rate, mask); + + if (!(I_CLOCK_MASK & mask)) { + if (I_PCLK_MASK & mask) + return core_get_rate(CORECLK_ID_PCLK); + else if (I_BCLK_MASK & mask) + return core_get_rate(CORECLK_ID_BCLK); + else + return clk->rate; + } + +next: + if (peri->in_mask & I_EXTCLK1_FORCE) { + start_src = 4; max_src = 5; + } + for (n = start_src ; max_src > n; n++) { + if (!(((mask & I_CLOCK_MASK) >> n) & 0x1)) + continue; + + if (n == I_EXT1_BIT) { + rate = peri->in_extclk_1; + } else if (n == I_EXT2_BIT) { + rate = peri->in_extclk_2; + } else { + pll = clk_dev_get(n); + rate = pll->clk.rate; + } + + if (!rate) + continue; + + for (i = 0; step > i ; i++) + rate = clk_divide(rate, request, 2, &div[i]); + + if (rate_hz && (abs(rate - request) > abs(rate_hz - request))) + continue; + + debug("clk: %s.%d, pll.%d[%lu] request[%ld] calc[%ld]\n", + peri->dev_name, peri->dev_id, n, pll->clk.rate, + request, rate); + + if (clk2) { + s1 = -1, d1 = -1; /* not use */ + s2 = n, d2 = div[0]; + } else { + s1 = n, d1 = div[0]; + s2 = I_CLKn_BIT, d2 = div[1]; + } + rate_hz = rate; + } + + /* search 2th clock from input */ + if (!clk2 && abs(rate_hz - request) && + peri->in_mask1 & ((1 << I_CLOCK_NUM) - 1)) { + clk2 = 1; + mask = peri->in_mask1; + step = 1; + goto next; + } + if (peri->in_mask & I_EXTCLK1_FORCE) { + if (s1 == 0) { + s1 = 4; s2 = 7; + d1 = 1; d2 = 1; + } + } + + peri->div_src_0 = s1, peri->div_val_0 = d1; + peri->div_src_1 = s2, peri->div_val_1 = d2; + clk->rate = rate_hz; + + debug("clk: %s.%d, step[%d] src[%d,%d] %ld", peri->dev_name, + peri->dev_id, peri->clk_step, peri->div_src_0, peri->div_src_1, + rate); + debug("/(div0: %d * div1: %d) = %ld, %ld diff (%ld)\n", + peri->div_val_0, peri->div_val_1, rate_hz, request, + abs(rate_hz - request)); + + return clk->rate; +} + +unsigned long clk_get_rate(struct clk *clk) +{ + struct clk_dev *cdev = clk_container(clk); + + if (cdev->link) + clk = cdev->link; + return clk->rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + struct clk_dev *cdev = clk_container(clk); + struct clk_dev_peri *peri = cdev->peri; + int i; + + if (!peri) + return core_set_rate(clk, rate); + + clk_round_rate(clk, rate); + + for (i = 0; peri->clk_step > i ; i++) { + int s = (i == 0 ? peri->div_src_0 : peri->div_src_1); + int d = (i == 0 ? peri->div_val_0 : peri->div_val_1); + + if (-1 == s) + continue; + + clk_dev_rate(peri->base, i, s, d); + + debug("clk: %s.%d (%p) set_rate [%d] src[%d] div[%d]\n", + peri->dev_name, peri->dev_id, peri->base, i, s, d); + } + + return clk->rate; +} + +int clk_enable(struct clk *clk) +{ + struct clk_dev *cdev = clk_container(clk); + struct clk_dev_peri *peri = cdev->peri; + int i = 0, inv = 0; + + if (!peri) + return 0; + + debug("clk: %s.%d enable (BCLK=%s, PCLK=%s)\n", peri->dev_name, + peri->dev_id, I_GATE_BCLK & peri->in_mask ? "ON" : "PASS", + I_GATE_PCLK & peri->in_mask ? "ON" : "PASS"); + + if (!(I_CLOCK_MASK & peri->in_mask)) { + /* Gated BCLK/PCLK enable */ + if (I_GATE_BCLK & peri->in_mask) + clk_dev_bclk(peri->base, 1); + + if (I_GATE_PCLK & peri->in_mask) + clk_dev_pclk(peri->base, 1); + + return 0; + } + + /* invert */ + inv = peri->invert_0; + for (; peri->clk_step > i; i++, inv = peri->invert_1) + clk_dev_inv(peri->base, i, inv); + + /* Gated BCLK/PCLK enable */ + if (I_GATE_BCLK & peri->in_mask) + clk_dev_bclk(peri->base, 1); + + if (I_GATE_PCLK & peri->in_mask) + clk_dev_pclk(peri->base, 1); + + /* restore clock rate */ + for (i = 0; peri->clk_step > i ; i++) { + int s = (i == 0 ? peri->div_src_0 : peri->div_src_1); + int d = (i == 0 ? peri->div_val_0 : peri->div_val_1); + + if (s == -1) + continue; + clk_dev_rate(peri->base, i, s, d); + } + + clk_dev_enb(peri->base, 1); + + return 0; +} + +void clk_disable(struct clk *clk) +{ + struct clk_dev *cdev = clk_container(clk); + struct clk_dev_peri *peri = cdev->peri; + + if (!peri) + return; + + debug("clk: %s.%d disable\n", peri->dev_name, peri->dev_id); + + if (!(I_CLOCK_MASK & peri->in_mask)) { + /* Gated BCLK/PCLK disable */ + if (I_GATE_BCLK & peri->in_mask) + clk_dev_bclk(peri->base, 0); + + if (I_GATE_PCLK & peri->in_mask) + clk_dev_pclk(peri->base, 0); + + return; + } + + clk_dev_rate(peri->base, 0, 7, 256); /* for power save */ + clk_dev_enb(peri->base, 0); + + /* Gated BCLK/PCLK disable */ + if (I_GATE_BCLK & peri->in_mask) + clk_dev_bclk(peri->base, 0); + + if (I_GATE_PCLK & peri->in_mask) + clk_dev_pclk(peri->base, 0); +} + +/* + * Core clocks APIs + */ +void __init clk_init(void) +{ + struct clk_dev *cdev = st_clk_devs; + struct clk_dev_peri *peri = clk_periphs; + struct clk *clk = NULL; + int i = 0; + + memset(cdev, 0, sizeof(st_clk_devs)); + core_rate_init(); + + for (i = 0; (CLK_CORE_NUM + CLK_PERI_NUM) > i; i++, cdev++) { + if (i < CLK_CORE_NUM) { + cdev->name = clk_core[i]; + clk = &cdev->clk; + clk->rate = core_get_rate(i); + continue; + } + + peri = &clk_periphs[i - CLK_CORE_NUM]; + peri->base = (void *)peri->base; + + cdev->peri = peri; + cdev->name = peri->dev_name; + + if (!(I_CLOCK_MASK & peri->in_mask)) { + if (I_BCLK_MASK & peri->in_mask) + cdev->clk.rate = core_get_rate(CORECLK_ID_BCLK); + if (I_PCLK_MASK & peri->in_mask) + cdev->clk.rate = core_get_rate(CORECLK_ID_PCLK); + } + + /* prevent uart clock disable for low step debug message */ + #ifndef CONFIG_DEBUG_NX_UART + if (peri->dev_name) { + #ifdef CONFIG_BACKLIGHT_PWM + if (!strcmp(peri->dev_name, DEV_NAME_PWM)) + continue; + #endif + } + #endif + } + debug("CPU : Clock Generator= %d EA, ", CLK_DEVS_NUM); +} diff --git a/arch/arm/mach-nexell/cmd_boot_linux.c b/arch/arm/mach-nexell/cmd_boot_linux.c new file mode 100644 index 0000000..f9d2a5c --- /dev/null +++ b/arch/arm/mach-nexell/cmd_boot_linux.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 nexell + * jhkim jhkim@nexell.co.kr + */ + +#include <common.h> +#include <bootm.h> +#include <command.h> +#include <environment.h> +#include <errno.h> +#include <image.h> +#include <fdt_support.h> + +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_CLI_FRAMEWORK) + +DECLARE_GLOBAL_DATA_PTR; + +static bootm_headers_t linux_images; + +static void boot_go_set_os(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[], + bootm_headers_t *images) +{ + char * const img_addr = argv[0]; + + images->os.type = IH_TYPE_KERNEL; + images->os.comp = IH_COMP_NONE; + images->os.os = IH_OS_LINUX; + images->os.load = simple_strtoul(img_addr, NULL, 16); + images->ep = images->os.load; +#if defined(CONFIG_ARM) + images->os.arch = IH_ARCH_ARM; +#elif defined(CONFIG_ARM64) + images->os.arch = IH_ARCH_ARM64; +#else + #error "Not support architecture ..." +#endif + +#if !defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_SPL_BUILD) + /* set DTB address for linux kernel */ + if (argc > 2) { + unsigned long ft_addr; + + ft_addr = simple_strtol(argv[2], NULL, 16); + images->ft_addr = (char *)ft_addr; + + /* + * if not defined IMAGE_ENABLE_OF_LIBFDT, + * must be set to fdt address + */ + if (!IMAGE_ENABLE_OF_LIBFDT) + gd->bd->bi_boot_params = ft_addr; + + debug("## set ft:%08lx and boot params:%08lx [control of:%s]" + "...\n", ft_addr, gd->bd->bi_boot_params, + IMAGE_ENABLE_OF_LIBFDT ? "on" : "off"); + } +#endif +} + +#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB) +static void boot_start_lmb(bootm_headers_t *images) +{ + ulong mem_start; + phys_size_t mem_size; + + lmb_init(&images->lmb); + + mem_start = getenv_bootm_low(); + mem_size = getenv_bootm_size(); + + lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size); + + arch_lmb_reserve(&images->lmb); + board_lmb_reserve(&images->lmb); +} +#else +#define lmb_reserve(lmb, base, size) +static inline void boot_start_lmb(bootm_headers_t *images) { } +#endif + +int do_boot_linux(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + boot_os_fn *boot_fn; + bootm_headers_t *images = &linux_images; + int flags; + int ret; + + boot_start_lmb(images); + + flags = BOOTM_STATE_START; + + argc--; argv++; + boot_go_set_os(cmdtp, flag, argc, argv, images); + +#if defined(CONFIG_OF_LIBFDT) + /* find flattened device tree */ + ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, images, + &images->ft_addr, &images->ft_len); + if (ret) { + puts("Could not find a valid device tree\n"); + return 1; + } + set_working_fdt_addr((ulong)images->ft_addr); +#endif +#if !defined(CONFIG_OF_LIBFDT) + flags |= BOOTM_STATE_OS_GO; +#endif + + boot_fn = do_bootm_linux; + ret = boot_fn(flags, argc, argv, images); + + if (ret == BOOTM_ERR_UNIMPLEMENTED) + show_boot_progress(BOOTSTAGE_ID_DECOMP_UNIMPL); + else if (ret == BOOTM_ERR_RESET) + do_reset(cmdtp, flag, argc, argv); + + return ret; +} + +U_BOOT_CMD(bootl, CONFIG_SYS_MAXARGS, 1, do_boot_linux, + "boot linux image from memory", + "[addr [arg ...]]\n - boot linux image stored in memory\n" + "\tuse a '-' for the DTB address\n" +); +#endif + +#if defined(CONFIG_CMD_BOOTD) && !defined(CONFIG_CMD_BOOTM) +int do_bootd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return run_command(env_get("bootcmd"), flag); +} + +U_BOOT_CMD(boot, 1, 1, do_bootd, + "boot default, i.e., run 'bootcmd'", + "" +); + +/* keep old command name "bootd" for backward compatibility */ +U_BOOT_CMD(bootd, 1, 1, do_bootd, + "boot default, i.e., run 'bootcmd'", + "" +); +#endif diff --git a/arch/arm/mach-nexell/config.mk b/arch/arm/mach-nexell/config.mk new file mode 100644 index 0000000..7b06626 --- /dev/null +++ b/arch/arm/mach-nexell/config.mk @@ -0,0 +1,11 @@ +# +# (C) Copyright 2016 Nexell +# junghyun kimjhkim@nexell.co.kr +# +# SPDX-License-Identifier: GPL-2.0+ +# + +SOCDIR=CPUDIR/$(VENDOR) +MACHDIR=$(patsubst %,arch/arm/mach-%,$(machine-y)) + +LDPPFLAGS += -DMACHDIR=$(MACHDIR) -DSOCDIR=$(SOCDIR) diff --git a/arch/arm/mach-nexell/nx_gpio.c b/arch/arm/mach-nexell/nx_gpio.c new file mode 100644 index 0000000..dfba3a2 --- /dev/null +++ b/arch/arm/mach-nexell/nx_gpio.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Youngbok, Park ybpark@nexell.co.kr + */ + +/* + * FIXME : will be remove after support pinctrl + */ +#include <linux/types.h> +#include <asm/io.h> +#include <asm/arch/nexell.h> +#include "asm/arch/nx_gpio.h" +#define NUMBER_OF_GPIO_MODULE 5 +u32 __g_nx_gpio_valid_bit[NUMBER_OF_GPIO_MODULE] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; + +static struct { + struct nx_gpio_register_set *pregister; +} __g_module_variables[NUMBER_OF_GPIO_MODULE] = { + { (struct nx_gpio_register_set *)PHY_BASEADDR_GPIOA }, + { (struct nx_gpio_register_set *)PHY_BASEADDR_GPIOB }, + { (struct nx_gpio_register_set *)PHY_BASEADDR_GPIOC }, + { (struct nx_gpio_register_set *)PHY_BASEADDR_GPIOD }, + { (struct nx_gpio_register_set *)PHY_BASEADDR_GPIOE }, +}; + +enum { nx_gpio_max_bit = 32 }; + +void nx_gpio_set_bit(u32 *value, u32 bit, int enable) +{ + register u32 newvalue; + + newvalue = *value; + newvalue &= ~(1ul << bit); + newvalue |= (u32)enable << bit; + writel(newvalue, value); +} + +int nx_gpio_get_bit(u32 value, u32 bit) +{ + return (int)((value >> bit) & (1ul)); +} + +void nx_gpio_set_bit2(u32 *value, u32 bit, u32 bit_value) +{ + register u32 newvalue = *value; + + newvalue = (u32)(newvalue & ~(3ul << (bit * 2))); + newvalue = (u32)(newvalue | (bit_value << (bit * 2))); + + writel(newvalue, value); +} + +u32 nx_gpio_get_bit2(u32 value, u32 bit) +{ + return (u32)((u32)(value >> (bit * 2)) & 3ul); +} + +int nx_gpio_initialize(void) +{ + static int binit; + u32 i; + + binit = 0; + + if (binit == 0) { + for (i = 0; i < NUMBER_OF_GPIO_MODULE; i++) + __g_module_variables[i].pregister = NULL; + binit = true; + } + for (i = 0; i < NUMBER_OF_GPIO_MODULE; i++) { + __g_nx_gpio_valid_bit[i] = 0xFFFFFFFF; + }; + return true; +} + +u32 nx_gpio_get_number_of_module(void) +{ + return NUMBER_OF_GPIO_MODULE; +} + +u32 nx_gpio_get_size_of_register_set(void) +{ + return sizeof(struct nx_gpio_register_set); +} + +void nx_gpio_set_base_address(u32 module_index, void *base_address) +{ + __g_module_variables[module_index].pregister = + (struct nx_gpio_register_set *)base_address; +} + +void *nx_gpio_get_base_address(u32 module_index) +{ + return (void *)__g_module_variables[module_index].pregister; +} + +int nx_gpio_open_module(u32 module_index) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(0xFFFFFFFF, &pregister->gpiox_slew_disable_default); + writel(0xFFFFFFFF, &pregister->gpiox_drv1_disable_default); + writel(0xFFFFFFFF, &pregister->gpiox_drv0_disable_default); + writel(0xFFFFFFFF, &pregister->gpiox_pullsel_disable_default); + writel(0xFFFFFFFF, &pregister->gpiox_pullenb_disable_default); + return true; +} + +int nx_gpio_close_module(u32 module_index) { return true; } + +int nx_gpio_check_busy(u32 module_index) { return false; } + +void nx_gpio_set_pad_function(u32 module_index, u32 bit_number, + u32 padfunc) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + nx_gpio_set_bit2(&pregister->gpioxaltfn[bit_number / 16], + bit_number % 16, padfunc); +} + +void nx_gpio_set_pad_function32(u32 module_index, u32 msbvalue, u32 lsbvalue) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(lsbvalue, &pregister->gpioxaltfn[0]); + writel(msbvalue, &pregister->gpioxaltfn[1]); +} + +int nx_gpio_get_pad_function(u32 module_index, u32 bit_number) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return (int)nx_gpio_get_bit2 + (readl(&pregister->gpioxaltfn[bit_number / 16]), + bit_number % 16); +} + +void nx_gpio_set_output_enable(u32 module_index, u32 bit_number, + int output_enb) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + nx_gpio_set_bit(&pregister->gpioxoutenb, bit_number, output_enb); +} + +int nx_gpio_get_detect_enable(u32 module_index, u32 bit_number) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return nx_gpio_get_bit(readl(&pregister->gpioxdetenb), bit_number); +} + +u32 nx_gpio_get_detect_enable32(u32 module_index) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return readl(&pregister->gpioxdetenb); +} + +void nx_gpio_set_detect_enable(u32 module_index, u32 bit_number, + int detect_enb) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + nx_gpio_set_bit(&pregister->gpioxdetenb, bit_number, detect_enb); +} + +void nx_gpio_set_detect_enable32(u32 module_index, u32 enable_flag) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(enable_flag, &pregister->gpioxdetenb); +} + +int nx_gpio_get_output_enable(u32 module_index, u32 bit_number) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return nx_gpio_get_bit(readl(&pregister->gpioxoutenb), bit_number); +} + +void nx_gpio_set_output_enable32(u32 module_index, int output_enb) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (output_enb) + writel(0xFFFFFFFF, &pregister->gpioxoutenb); + else + writel(0x0, &pregister->gpioxoutenb); +} + +u32 nx_gpio_get_output_enable32(u32 module_index) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return readl(&pregister->gpioxoutenb); +} + +void nx_gpio_set_output_value(u32 module_index, u32 bit_number, int value) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + nx_gpio_set_bit(&pregister->gpioxout, bit_number, value); +} + +int nx_gpio_get_output_value(u32 module_index, u32 bit_number) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return nx_gpio_get_bit(readl(&pregister->gpioxout), bit_number); +} + +void nx_gpio_set_output_value32(u32 module_index, u32 value) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(value, &pregister->gpioxout); +} + +u32 nx_gpio_get_output_value32(u32 module_index) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return readl(&pregister->gpioxout); +} + +int nx_gpio_get_input_value(u32 module_index, u32 bit_number) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return nx_gpio_get_bit(readl(&pregister->gpioxpad), bit_number); +} + +void nx_gpio_set_pull_select(u32 module_index, u32 bit_number, int enable) +{ + nx_gpio_set_bit(&__g_module_variables[module_index] + .pregister->gpiox_pullsel_disable_default, + bit_number, true); + nx_gpio_set_bit + (&__g_module_variables[module_index].pregister->gpiox_pullsel, + bit_number, enable); +} + +void nx_gpio_set_pull_select32(u32 module_index, u32 value) +{ + writel(value, + &__g_module_variables[module_index].pregister->gpiox_pullsel); +} + +int nx_gpio_get_pull_select(u32 module_index, u32 bit_number) +{ + return nx_gpio_get_bit + (__g_module_variables[module_index].pregister->gpiox_pullsel, + bit_number); +} + +u32 nx_gpio_get_pull_select32(u32 module_index) +{ + return __g_module_variables[module_index].pregister->gpiox_pullsel; +} + +void nx_gpio_set_pull_mode(u32 module_index, u32 bit_number, u32 mode) +{ + nx_gpio_set_bit(&__g_module_variables[module_index] + .pregister->gpiox_pullsel_disable_default, + bit_number, true); + nx_gpio_set_bit(&__g_module_variables[module_index] + .pregister->gpiox_pullenb_disable_default, + bit_number, true); + if (mode == nx_gpio_pull_off) { + nx_gpio_set_bit + (&__g_module_variables[module_index].pregister->gpiox_pullenb, + bit_number, false); + nx_gpio_set_bit + (&__g_module_variables[module_index].pregister->gpiox_pullsel, + bit_number, false); + } else { + nx_gpio_set_bit + (&__g_module_variables[module_index].pregister->gpiox_pullsel, + bit_number, (mode & 1 ? true : false)); + nx_gpio_set_bit + (&__g_module_variables[module_index].pregister->gpiox_pullenb, + bit_number, true); + } +} + +void nx_gpio_set_fast_slew(u32 module_index, u32 bit_number, + int enable) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + nx_gpio_set_bit(&pregister->gpiox_slew, bit_number, + (int)(!enable)); +} + +void nx_gpio_set_drive_strength(u32 module_index, u32 bit_number, + u32 drvstrength) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + nx_gpio_set_bit(&pregister->gpiox_drv1, bit_number, + (int)(((u32)drvstrength >> 0) & 0x1)); + nx_gpio_set_bit(&pregister->gpiox_drv0, bit_number, + (int)(((u32)drvstrength >> 1) & 0x1)); +} + +void nx_gpio_set_drive_strength_disable_default(u32 module_index, + u32 bit_number, int enable) +{ + register struct nx_gpio_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + nx_gpio_set_bit(&pregister->gpiox_drv1_disable_default, bit_number, + (int)(enable)); + nx_gpio_set_bit(&pregister->gpiox_drv0_disable_default, bit_number, + (int)(enable)); +} + +u32 nx_gpio_get_drive_strength(u32 module_index, u32 bit_number) +{ + register struct nx_gpio_register_set *pregister; + register u32 retvalue; + + pregister = __g_module_variables[module_index].pregister; + retvalue = + nx_gpio_get_bit(readl(&pregister->gpiox_drv0), bit_number) << 1; + retvalue |= + nx_gpio_get_bit(readl(&pregister->gpiox_drv1), bit_number) << 0; + return retvalue; +} diff --git a/arch/arm/mach-nexell/nx_sec_reg.c b/arch/arm/mach-nexell/nx_sec_reg.c new file mode 100644 index 0000000..3d3dd9f --- /dev/null +++ b/arch/arm/mach-nexell/nx_sec_reg.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Youngbok, Park park@nexell.co.kr + */ + +#include <linux/types.h> +#include <asm/io.h> +#include <asm/arch/nexell.h> +#include <asm/arch/sec_reg.h> +#include <linux/linkage.h> + +#define NEXELL_SMC_BASE 0x82000000 + +#define NEXELL_SMC_FN(n) (NEXELL_SMC_BASE + (n)) + +#define NEXELL_SMC_SEC_REG_WRITE NEXELL_SMC_FN(0x0) +#define NEXELL_SMC_SEC_REG_READ NEXELL_SMC_FN(0x1) + +#define SECURE_ID_SHIFT 8 + +#define SEC_4K_OFFSET ((4 * 1024) - 1) +#define SEC_64K_OFFSET ((64 * 1024) - 1) + +asmlinkage int __invoke_nexell_fn_smc(u32, u32, u32, u32); + +int write_sec_reg_by_id(void __iomem *reg, int val, int id) +{ + int ret = 0; + u32 off = 0; + + switch (id) { + case NEXELL_L2C_SEC_ID: + case NEXELL_MIPI_SEC_ID: + case NEXELL_TOFF_SEC_ID: + off = (u32)reg & SEC_4K_OFFSET; + break; + case NEXELL_MALI_SEC_ID: + off = (u32)reg & SEC_64K_OFFSET; + break; + } + ret = __invoke_nexell_fn_smc(NEXELL_SMC_SEC_REG_WRITE | + ((1 << SECURE_ID_SHIFT) + id), off, val, 0); + return ret; +} + +int read_sec_reg_by_id(void __iomem *reg, int id) +{ + int ret = 0; + u32 off = 0; + + switch (id) { + case NEXELL_L2C_SEC_ID: + case NEXELL_MIPI_SEC_ID: + case NEXELL_TOFF_SEC_ID: + off = (u32)reg & SEC_4K_OFFSET; + break; + case NEXELL_MALI_SEC_ID: + off = (u32)reg & SEC_64K_OFFSET; + break; + } + ret = __invoke_nexell_fn_smc(NEXELL_SMC_SEC_REG_READ | + ((1 << SECURE_ID_SHIFT) + id), off, 0, 0); + return ret; +} + +int write_sec_reg(void __iomem *reg, int val) +{ + int ret = 0; + + ret = __invoke_nexell_fn_smc(NEXELL_SMC_SEC_REG_WRITE, + (u32)reg, val, 0); + return ret; +} + +int read_sec_reg(void __iomem *reg) +{ + int ret = 0; + + ret = __invoke_nexell_fn_smc(NEXELL_SMC_SEC_REG_READ, (u32)reg, 0, 0); + return ret; +} diff --git a/arch/arm/mach-nexell/reg-call.S b/arch/arm/mach-nexell/reg-call.S new file mode 100644 index 0000000..5fdf515 --- /dev/null +++ b/arch/arm/mach-nexell/reg-call.S @@ -0,0 +1,23 @@ +#include <asm-offsets.h> +#include <config.h> +#include <linux/linkage.h> + +#define ___asm_opcode_identity32(x) ((x) & 0xFFFFFFFF) +#define __opcode_to_mem_arm(x) ___opcode_identity32(x) +#define ___asm_opcode_to_mem_arm(x) ___asm_opcode_identity32(x) + +#define ___opcode_identity32(x) ((u32)(x)) +#define ___inst_arm(x) .long x +#define __inst_arm(x) ___inst_arm(___asm_opcode_to_mem_arm(x)) + +#define __inst_arm_thumb32(arm_opcode, thumb_opcode) __inst_arm(arm_opcode) + +#define __SMC(imm4) __inst_arm_thumb32( \ + 0xE1600070 | (((imm4) & 0xF) << 0), \ + 0xF7F08000 | (((imm4) & 0xF) << 16) \ +) + +ENTRY(__invoke_nexell_fn_smc) + __SMC(0) + bx lr +ENDPROC(__invoke_nexell_fn_smc) diff --git a/arch/arm/mach-nexell/reset.c b/arch/arm/mach-nexell/reset.c new file mode 100644 index 0000000..1f732a3 --- /dev/null +++ b/arch/arm/mach-nexell/reset.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Youngbok, Park park@nexell.co.kr + */ + +/* + *FIXME : Not support device tree & reset control driver. + * will remove after support device tree & reset control driver. + */ +#include <common.h> +#include <asm/io.h> +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> + +struct nx_rstcon_registerset { + u32 regrst[(NUMBER_OF_RESET_MODULE_PIN + 31) >> 5]; +}; + +static struct nx_rstcon_registerset *nx_rstcon = + (struct nx_rstcon_registerset *)PHY_BASEADDR_RSTCON; + +void nx_rstcon_setrst(u32 rstindex, enum rstcon status) +{ + u32 regnum, bitpos, curstat; + + regnum = rstindex >> 5; + curstat = (u32)readl(&nx_rstcon->regrst[regnum]); + bitpos = rstindex & 0x1f; + curstat &= ~(1UL << bitpos); + curstat |= (status & 0x01) << bitpos; + writel(curstat, &nx_rstcon->regrst[regnum]); +} diff --git a/arch/arm/mach-nexell/serial.c b/arch/arm/mach-nexell/serial.c new file mode 100644 index 0000000..c137922 --- /dev/null +++ b/arch/arm/mach-nexell/serial.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Hyunseok, Jung hsjung@nexell.co.kr + */ + +#include <common.h> +#include <linux/compiler.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include <serial.h> +#include <asm/arch/nexell.h> +#include <asm/arch/clk.h> + +/* baudrate rest value */ +union br_rest { + unsigned short slot; /* udivslot */ + unsigned char value; /* ufracval */ +}; + +struct s5p_uart { + unsigned int ulcon; + unsigned int ucon; + unsigned int ufcon; + unsigned int umcon; + unsigned int utrstat; + unsigned int uerstat; + unsigned int ufstat; + unsigned int umstat; + unsigned char utxh; + unsigned char res1[3]; + unsigned char urxh; + unsigned char res2[3]; + unsigned int ubrdiv; + union br_rest rest; + unsigned char res3[0x3d0]; +}; + +static inline int s5p_uart_divslot(void) +{ + return 1; +} + +DECLARE_GLOBAL_DATA_PTR; + +#define CONSOLE_PORT CONFIG_S5P_SERIAL_INDEX +#if (0 == CONSOLE_PORT) +#define CONFIG_S5P_SERIAL_PORT (void *)PHY_BASEADDR_UART0 +#elif (1 == CONSOLE_PORT) +#define CONFIG_S5P_SERIAL_PORT (void *)PHY_BASEADDR_UART1 +#elif (2 == CONSOLE_PORT) +#define CONFIG_S5P_SERIAL_PORT (void *)PHY_BASEADDR_UART2 +#elif (3 == CONSOLE_PORT) +#define CONFIG_S5P_SERIAL_PORT (void *)PHY_BASEADDR_UART3 +#elif (4 == CONSOLE_PORT) +#define CONFIG_S5P_SERIAL_PORT (void *)PHY_BASEADDR_UART4 +#elif (5 == CONSOLE_PORT) +#define CONFIG_S5P_SERIAL_PORT (void *)PHY_BASEADDR_UART5 +#endif +static unsigned char *const port = CONFIG_S5P_SERIAL_PORT; +static unsigned int const clock_in = CONFIG_S5P_SERIAL_CLOCK; + +#define RX_FIFO_COUNT_MASK 0xff +#define RX_FIFO_FULL_MASK (1 << 8) +#define TX_FIFO_FULL_MASK (1 << 24) + +static void __serial_device_init(void) +{ + char dev[10]; + + sprintf(dev, "nx-uart.%d", CONFIG_S5P_SERIAL_INDEX); + + struct clk *clk = clk_get((const char *)dev); + + /* set clock */ + clk_disable(clk); + clk_set_rate(clk, CONFIG_UART_CLKGEN_CLOCK_HZ); + clk_enable(clk); +} + +void serial_device_init(void) + __attribute__((weak, alias("__serial_device_init"))); + +static inline struct s5p_uart *s5p_get_base_uart(int dev_index) +{ + return (struct s5p_uart *)port; +} + +/* + * The coefficient, used to calculate the baudrate on S5P UARTs is + * calculated as + * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT + * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, + * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: + */ +static const int udivslot[] = { + 0, + 0x0080, + 0x0808, + 0x0888, + 0x2222, + 0x4924, + 0x4a52, + 0x54aa, + 0x5555, + 0xd555, + 0xd5d5, + 0xddd5, + 0xdddd, + 0xdfdd, + 0xdfdf, + 0xffdf, +}; + +static void serial_setbrg_dev(const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + u32 uclk = clock_in; + u32 baudrate = gd->baudrate; + u32 val; + + val = uclk / baudrate; + + writel(val / 16 - 1, &uart->ubrdiv); + + if (s5p_uart_divslot()) + writew(udivslot[val % 16], &uart->rest.slot); + else + writeb(val % 16, &uart->rest.value); +} + +/* + * Initialise the serial port with the given baudrate. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +static int serial_init_dev(const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + + serial_device_init(); + + /* enable FIFOs, auto clear Rx FIFO */ + writel(0x3, &uart->ufcon); + writel(0, &uart->umcon); + /* 8N1 */ + writel(0x3, &uart->ulcon); + /* No interrupts, no DMA, pure polling */ + writel(0x245, &uart->ucon); + + serial_setbrg_dev(dev_index); + + return 0; +} + +static int serial_err_check(const int dev_index, int op) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + unsigned int mask; + + /* + * UERSTAT + * Break Detect [3] + * Frame Err [2] : receive operation + * Parity Err [1] : receive operation + * Overrun Err [0] : receive operation + */ + if (op) + mask = 0x8; + else + mask = 0xf; + + return readl(&uart->uerstat) & mask; +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is succesfull, the character read is + * written into its argument c. + */ +static int serial_getc_dev(const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + + /* wait for character to arrive */ + while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | + RX_FIFO_FULL_MASK))) { + if (serial_err_check(dev_index, 0)) + return 0; + } + + return (int)(readb(&uart->urxh) & 0xff); +} + +/* + * Output a single byte to the serial port. + */ +static void serial_putc_dev(const char c, const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + + /* wait for room in the tx FIFO */ + while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { + if (serial_err_check(dev_index, 1)) + return; + } + + writeb(c, &uart->utxh); + + /* If \n, also do \r */ + if (c == '\n') + serial_putc('\r'); +} + +/* + * Test whether a character is in the RX buffer + */ +static int serial_tstc_dev(const int dev_index) +{ + struct s5p_uart *const uart = s5p_get_base_uart(dev_index); + + return (int)(readl(&uart->utrstat) & 0x1); +} + +static void serial_puts_dev(const char *s, const int dev_index) +{ + while (*s) + serial_putc_dev(*s++, dev_index); +} + +static int s5p_serial_init(void) +{ return serial_init_dev(CONSOLE_PORT); } +static void s5p_serial_setbrg(void) +{ serial_setbrg_dev(CONSOLE_PORT); } +static int s5p_serial_getc(void) +{ return serial_getc_dev(CONSOLE_PORT); } +static int s5p_serial_tstc(void) +{ return serial_tstc_dev(CONSOLE_PORT); } +static void s5p_serial_putc(const char c) +{ serial_putc_dev(c, CONSOLE_PORT); } +static void s5p_serial_puts(const char *s) +{ serial_puts_dev(s, CONSOLE_PORT); } + +static struct serial_device s5p_serial_device = { + .name = "serial_s5p", + .start = s5p_serial_init, + .stop = NULL, + .setbrg = s5p_serial_setbrg, + .getc = s5p_serial_getc, + .tstc = s5p_serial_tstc, + .putc = s5p_serial_putc, + .puts = s5p_serial_puts, +}; + +__weak struct serial_device *default_serial_console(void) +{ + return &s5p_serial_device; +} + +void s5p_serial_initialize(void) +{ + serial_register(&s5p_serial_device); +} diff --git a/arch/arm/mach-nexell/tieoff.c b/arch/arm/mach-nexell/tieoff.c new file mode 100644 index 0000000..9a89517 --- /dev/null +++ b/arch/arm/mach-nexell/tieoff.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Youngbok, Park park@nexell.co.kr + */ + +#include <common.h> +#include <asm/arch/nexell.h> +#include <asm/arch/clk.h> +#include <asm/arch/reset.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/tieoff.h> +#include <asm/arch/sec_reg.h> + +#define NX_PIN_FN_SIZE 4 +#define TIEOFF_REG_NUM 33 + +struct nx_tieoff_registerset { + u32 tieoffreg[TIEOFF_REG_NUM]; +}; + +static struct nx_tieoff_registerset *nx_tieoff = (void *)PHY_BASEADDR_TIEOFF; + +static int tieoff_readl(void __iomem *reg) +{ +#ifdef CONFIG_ARCH_S5P4418 + return read_sec_reg_by_id(reg, NEXELL_TOFF_SEC_ID); +#else + return readl(reg); +#endif +} + +static int tieoff_writetl(void __iomem *reg, int val) +{ +#ifdef CONFIG_ARCH_S5P4418 + return write_sec_reg_by_id(reg, val, NEXELL_TOFF_SEC_ID); +#else + return writel(val, reg); +#endif +} + +void nx_tieoff_set(u32 tieoff_index, u32 tieoff_value) +{ + u32 regindex, mask; + u32 lsb, msb; + u32 regval; + + u32 position; + u32 bitwidth; + + position = tieoff_index & 0xffff; + bitwidth = (tieoff_index >> 16) & 0xffff; + + regindex = position >> 5; + + lsb = position & 0x1F; + msb = lsb + bitwidth; + + if (msb > 32) { + msb &= 0x1F; + mask = ~(0xffffffff << lsb); + regval = tieoff_readl(&nx_tieoff->tieoffreg[regindex]) & mask; + regval |= ((tieoff_value & ((1UL << bitwidth) - 1)) << lsb); + tieoff_writetl(&nx_tieoff->tieoffreg[regindex], regval); + + mask = (0xffffffff << msb); + regval = tieoff_readl(&nx_tieoff->tieoffreg[regindex]) & mask; + regval |= ((tieoff_value & ((1UL << bitwidth) - 1)) >> msb); + tieoff_writetl(&nx_tieoff->tieoffreg[regindex + 1], regval); + } else { + mask = (0xffffffff << msb) | (~(0xffffffff << lsb)); + regval = tieoff_readl(&nx_tieoff->tieoffreg[regindex]) & mask; + regval |= ((tieoff_value & ((1UL << bitwidth) - 1)) << lsb); + tieoff_writetl(&nx_tieoff->tieoffreg[regindex], regval); + } +} + +u32 nx_tieoff_get(u32 tieoff_index) +{ + u32 regindex, mask; + u32 lsb, msb; + u32 regval; + + u32 position; + u32 bitwidth; + + position = tieoff_index & 0xffff; + bitwidth = (tieoff_index >> 16) & 0xffff; + + regindex = position / 32; + lsb = position % 32; + msb = lsb + bitwidth; + + if (msb > 32) { + msb &= 0x1F; + mask = 0xffffffff << lsb; + regval = tieoff_readl(&nx_tieoff->tieoffreg[regindex]) & mask; + regval >>= lsb; + + mask = ~(0xffffffff << msb); + regval |= ((tieoff_readl(&nx_tieoff->tieoffreg[regindex + 1]) + & mask) << (32 - lsb)); + } else { + mask = ~(0xffffffff << msb) & (0xffffffff << lsb); + regval = tieoff_readl(&nx_tieoff->tieoffreg[regindex]) & mask; + regval >>= lsb; + } + return regval; +} diff --git a/arch/arm/mach-nexell/timer.c b/arch/arm/mach-nexell/timer.c new file mode 100644 index 0000000..f915684 --- /dev/null +++ b/arch/arm/mach-nexell/timer.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Hyunseok, Jung hsjung@nexell.co.kr + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/nexell.h> +#include <asm/arch/clk.h> +#if defined(CONFIG_ARCH_S5P4418) +#include <asm/arch/reset.h> +#endif + +#if (CONFIG_TIMER_SYS_TICK_CH > 3) +#error Not support timer channel. Please use "0~3" channels. +#endif + +/* global variables to save timer count + * + * Section ".data" must be used because BSS is not available before relocation, + * in board_init_f(), respectively! I.e. global variables can not be used! + */ +static unsigned long timestamp __attribute__ ((section(".data"))); +static unsigned long lastdec __attribute__ ((section(".data"))); +static int timerinit __attribute__ ((section(".data"))); + +/* macro to hw timer tick config */ +static long TIMER_FREQ = 1000000; +static long TIMER_HZ = 1000000 / CONFIG_SYS_HZ; +static long TIMER_COUNT = 0xFFFFFFFF; + +#define REG_TCFG0 (0x00) +#define REG_TCFG1 (0x04) +#define REG_TCON (0x08) +#define REG_TCNTB0 (0x0C) +#define REG_TCMPB0 (0x10) +#define REG_TCNT0 (0x14) +#define REG_CSTAT (0x44) + +#define TCON_BIT_AUTO (1 << 3) +#define TCON_BIT_INVT (1 << 2) +#define TCON_BIT_UP (1 << 1) +#define TCON_BIT_RUN (1 << 0) +#define TCFG0_BIT_CH(ch) ((ch) == 0 || (ch) == 1 ? 0 : 8) +#define TCFG1_BIT_CH(ch) ((ch) * 4) +#define TCON_BIT_CH(ch) ((ch) ? (ch) * 4 + 4 : 0) +#define TINT_CH(ch) (ch) +#define TINT_CSTAT_BIT_CH(ch) ((ch) + 5) +#define TINT_CSTAT_MASK (0x1F) +#define TIMER_TCNT_OFFS (0xC) + +void reset_timer_masked(void); +unsigned long get_timer_masked(void); + +/* + * Timer HW + */ +static inline void timer_clock(void __iomem *base, int ch, int mux, int scl) +{ + u32 val = readl(base + REG_TCFG0) & ~(0xFF << TCFG0_BIT_CH(ch)); + + writel(val | ((scl - 1) << TCFG0_BIT_CH(ch)), base + REG_TCFG0); + val = readl(base + REG_TCFG1) & ~(0xF << TCFG1_BIT_CH(ch)); + writel(val | (mux << TCFG1_BIT_CH(ch)), base + REG_TCFG1); +} + +static inline void timer_count(void __iomem *base, int ch, unsigned int cnt) +{ + writel((cnt - 1), base + REG_TCNTB0 + (TIMER_TCNT_OFFS * ch)); + writel((cnt - 1), base + REG_TCMPB0 + (TIMER_TCNT_OFFS * ch)); +} + +static inline void timer_start(void __iomem *base, int ch) +{ + int on = 0; + u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch); + + writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch), + base + REG_CSTAT); + val = readl(base + REG_TCON) & ~(0xE << TCON_BIT_CH(ch)); + writel(val | (TCON_BIT_UP << TCON_BIT_CH(ch)), base + REG_TCON); + + val &= ~(TCON_BIT_UP << TCON_BIT_CH(ch)); + val |= ((TCON_BIT_AUTO | TCON_BIT_RUN) << TCON_BIT_CH(ch)); + writel(val, base + REG_TCON); + dmb(); +} + +static inline void timer_stop(void __iomem *base, int ch) +{ + int on = 0; + u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch); + + writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch), + base + REG_CSTAT); + val = readl(base + REG_TCON) & ~(TCON_BIT_RUN << TCON_BIT_CH(ch)); + writel(val, base + REG_TCON); +} + +static inline unsigned long timer_read(void __iomem *base, int ch) +{ + unsigned long ret; + + ret = TIMER_COUNT - readl(base + REG_TCNT0 + (TIMER_TCNT_OFFS * ch)); + return ret; +} + +int timer_init(void) +{ + struct clk *clk = NULL; + char name[16] = "pclk"; + int ch = CONFIG_TIMER_SYS_TICK_CH; + unsigned long rate, tclk = 0; + unsigned long mout, thz, cmp = -1UL; + int tcnt, tscl = 0, tmux = 0; + int mux = 0, scl = 0; + void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER; + + if (timerinit) + return 0; + + /* get with PCLK */ + clk = clk_get(name); + rate = clk_get_rate(clk); + for (mux = 0; mux < 5; mux++) { + mout = rate / (1 << mux), scl = mout / TIMER_FREQ, + thz = mout / scl; + if (!(mout % TIMER_FREQ) && 256 > scl) { + tclk = thz, tmux = mux, tscl = scl; + break; + } + if (scl > 256) + continue; + if (abs(thz - TIMER_FREQ) >= cmp) + continue; + tclk = thz, tmux = mux, tscl = scl; + cmp = abs(thz - TIMER_FREQ); + } + tcnt = tclk; /* Timer Count := 1 Mhz counting */ + + TIMER_FREQ = tcnt; /* Timer Count := 1 Mhz counting */ + TIMER_HZ = TIMER_FREQ / CONFIG_SYS_HZ; + tcnt = TIMER_COUNT == 0xFFFFFFFF ? TIMER_COUNT + 1 : tcnt; + + timer_stop(base, ch); + timer_clock(base, ch, tmux, tscl); + timer_count(base, ch, tcnt); + timer_start(base, ch); + + reset_timer_masked(); + timerinit = 1; + + return 0; +} + +void reset_timer(void) +{ + reset_timer_masked(); +} + +unsigned long get_timer(unsigned long base) +{ + long ret; + unsigned long time = get_timer_masked(); + unsigned long hz = TIMER_HZ; + + ret = time / hz - base; + return ret; +} + +void set_timer(unsigned long t) +{ + timestamp = (unsigned long)t; +} + +void reset_timer_masked(void) +{ + void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER; + int ch = CONFIG_TIMER_SYS_TICK_CH; + + /* reset time */ + /* capure current decrementer value time */ + lastdec = timer_read(base, ch); + /* start "advancing" time stamp from 0 */ + timestamp = 0; +} + +unsigned long get_timer_masked(void) +{ + void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER; + int ch = CONFIG_TIMER_SYS_TICK_CH; + + unsigned long now = timer_read(base, ch); /* current tick value */ + + if (now >= lastdec) { /* normal mode (non roll) */ + /* move stamp fordward with absolute diff ticks */ + timestamp += now - lastdec; + } else { + /* we have overflow of the count down timer */ + /* nts = ts + ld + (TLV - now) + * ts=old stamp, ld=time that passed before passing through -1 + * (TLV-now) amount of time after passing though -1 + * nts = new "advancing time stamp"... + * it could also roll and cause problems. + */ + timestamp += now + TIMER_COUNT - lastdec; + } + /* save last */ + lastdec = now; + + debug("now=%lu, last=%lu, timestamp=%lu\n", now, lastdec, timestamp); + return (unsigned long)timestamp; +} + +void __udelay(unsigned long usec) +{ + unsigned long tmo, tmp; + + debug("+udelay=%ld\n", usec); + + if (!timerinit) + timer_init(); + + /* if "big" number, spread normalization to seconds */ + if (usec >= 1000) { + /* start to normalize for usec to ticks per sec */ + tmo = usec / 1000; + /* find number of "ticks" to wait to achieve target */ + tmo *= TIMER_FREQ; + /* finish normalize. */ + tmo /= 1000; + /* else small number, don't kill it prior to HZ multiply */ + } else { + tmo = usec * TIMER_FREQ; + tmo /= (1000 * 1000); + } + + tmp = get_timer_masked(); /* get current timestamp */ + debug("A. tmo=%ld, tmp=%ld\n", tmo, tmp); + + /* if setting this fordward will roll time stamp */ + if (tmp > (tmo + tmp + 1)) + /* reset "advancing" timestamp to 0, set lastdec value */ + reset_timer_masked(); + else + /* set advancing stamp wake up time */ + tmo += tmp; + + debug("B. tmo=%ld, tmp=%ld\n", tmo, tmp); + + /* loop till event */ + do { + tmp = get_timer_masked(); + } while (tmo > tmp); + debug("-udelay=%ld\n", usec); +} + +void udelay_masked(unsigned long usec) +{ + unsigned long tmo, endtime; + signed long diff; + + /* if "big" number, spread normalization to seconds */ + if (usec >= 1000) { + /* start to normalize for usec to ticks per sec */ + tmo = usec / 1000; + /* find number of "ticks" to wait to achieve target */ + tmo *= TIMER_FREQ; + /* finish normalize. */ + tmo /= 1000; + } else { /* else small number, don't kill it prior to HZ multiply */ + tmo = usec * TIMER_FREQ; + tmo /= (1000 * 1000); + } + + endtime = get_timer_masked() + tmo; + + do { + unsigned long now = get_timer_masked(); + + diff = endtime - now; + } while (diff >= 0); +} + +unsigned long long get_ticks(void) +{ + return get_timer_masked(); +} + +#if defined(CONFIG_ARCH_S5P4418) +ulong get_tbclk(void) +{ + ulong tbclk = TIMER_FREQ; + return tbclk; +} +#endif

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01: - i2c/nx_i2c.c: Some adaptions mainly because of changes in "struct udevice". - mmc: nexell_dw_mmc.c changed to nexell_dw_mmc_dm.c (switched to DM).
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/nx_gpio.c | 252 +++++++++++++++++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/nx_i2c.c | 537 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/nexell_dw_mmc_dm.c | 350 +++++++++++++++++++++++++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-nexell.c | 252 +++++++++++++++++++ drivers/pwm/pwm-nexell.h | 54 +++++ 12 files changed, 1473 insertions(+) create mode 100644 drivers/gpio/nx_gpio.c create mode 100644 drivers/i2c/nx_i2c.c create mode 100644 drivers/mmc/nexell_dw_mmc_dm.c create mode 100644 drivers/pwm/pwm-nexell.c create mode 100644 drivers/pwm/pwm-nexell.h
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1de6f52..febda89 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -421,4 +421,13 @@ config MT7621_GPIO help Say yes here to support MediaTek MT7621 compatible GPIOs.
+config NX_GPIO + bool "Nexell GPIO driver" + depends on DM_GPIO + help + Support GPIO access on Nexell SoCs. The GPIOs are arranged into + a number of banks (different for each SoC type) each with 32 GPIOs. + The GPIOs for a device are defined in the device tree with one node + for each bank. + endmenu diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 449046b..e3340de 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o +obj-$(CONFIG_NX_GPIO) += nx_gpio.o diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c new file mode 100644 index 0000000..86472f6 --- /dev/null +++ b/drivers/gpio/nx_gpio.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * DeokJin, Lee truevirtue@nexell.co.kr + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/gpio.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct nx_gpio_regs { + u32 data; /* Data register */ + u32 outputenb; /* Output Enable register */ + u32 detmode[2]; /* Detect Mode Register */ + u32 intenb; /* Interrupt Enable Register */ + u32 det; /* Event Detect Register */ + u32 pad; /* Pad Status Register */ +}; + +struct nx_alive_gpio_regs { + u32 pwrgate; /* Power Gating Register */ + u32 reserved0[28]; /* Reserved0 */ + u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */ + u32 outputenb; /* Alive GPIO Output Enable Register */ + u32 outputenb_read; /* Alive GPIO Output Read Register */ + u32 reserved1[3]; /* Reserved1 */ + u32 pad_reset; /* Alive GPIO Output Reset Register */ + u32 data; /* Alive GPIO Output Register */ + u32 pad_read; /* Alive GPIO Pad Read Register */ + u32 reserved2[33]; /* Reserved2 */ + u32 pad; /* Alive GPIO Input Value Register */ +}; + +struct nx_gpio_platdata { + void *regs; + int gpio_count; + const char *bank_name; +}; + +static int nx_alive_gpio_is_check(struct udevice *dev) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + const char *bank_name = plat->bank_name; + + if (!strcmp(bank_name, "gpio_alv")) + return 1; + + return 0; +} + +static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + + setbits_le32(®s->outputenb_reset, 1 << pin); + + return 0; +} + +static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + + if (val) + setbits_le32(®s->data, 1 << pin); + else + setbits_le32(®s->pad_reset, 1 << pin); + + setbits_le32(®s->outputenb, 1 << pin); + + return 0; +} + +static int nx_alive_gpio_get_value(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + unsigned int mask = 1UL << pin; + unsigned int value; + + value = (readl(®s->pad_read) & mask) >> pin; + + return value; +} + +static int nx_alive_gpio_set_value(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + + if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->pad_reset, 1 << pin); + + return 0; +} + +static int nx_alive_gpio_get_function(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + unsigned int mask = (1UL << pin); + unsigned int output; + + output = readl(®s->outputenb_read) & mask; + + if (output) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +} + +static int nx_gpio_direction_input(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + + if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_direction_input(dev, pin); + + clrbits_le32(®s->outputenb, 1 << pin); + + return 0; +} + +static int nx_gpio_direction_output(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + + if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_direction_output(dev, pin, val); + + if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin); + + setbits_le32(®s->outputenb, 1 << pin); + + return 0; +} + +static int nx_gpio_get_value(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + unsigned int mask = 1UL << pin; + unsigned int value; + + if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_get_value(dev, pin); + + value = (readl(®s->pad) & mask) >> pin; + + return value; +} + +static int nx_gpio_set_value(struct udevice *dev, unsigned int pin, int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + + if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_set_value(dev, pin, val); + + if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin); + + return 0; +} + +static int nx_gpio_get_function(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + unsigned int mask = (1UL << pin); + unsigned int output; + + if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_get_function(dev, pin); + + output = readl(®s->outputenb) & mask; + + if (output) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +} + +static int nx_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + + uc_priv->gpio_count = plat->gpio_count; + uc_priv->bank_name = plat->bank_name; + + return 0; +} + +static int nx_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + + plat->regs = map_physmem(devfdt_get_addr(dev), + sizeof(struct nx_gpio_regs), + MAP_NOCACHE); + plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->node.of_offset, + "nexell,gpio-bank-width", 32); + plat->bank_name = fdt_getprop(gd->fdt_blob, dev->node.of_offset, + "gpio-bank-name", NULL); + + return 0; +} + +static const struct dm_gpio_ops nx_gpio_ops = { + .direction_input = nx_gpio_direction_input, + .direction_output = nx_gpio_direction_output, + .get_value = nx_gpio_get_value, + .set_value = nx_gpio_set_value, + .get_function = nx_gpio_get_function, +}; + +static const struct udevice_id nx_gpio_ids[] = { + { .compatible = "nexell,nexell-gpio" }, + { } +}; + +U_BOOT_DRIVER(nx_gpio) = { + .name = "nx_gpio", + .id = UCLASS_GPIO, + .of_match = nx_gpio_ids, + .ops = &nx_gpio_ops, + .ofdata_to_platdata = nx_gpio_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct nx_gpio_platdata), + .probe = nx_gpio_probe, +}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 03d2fed..2cd0ed3 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -317,6 +317,15 @@ config SYS_MXC_I2C8_SLAVE MXC I2C8 Slave endif
+config SYS_I2C_NEXELL + bool "Nexell I2C driver" + depends on DM_I2C + help + Add support for the Nexell I2C driver. This is used with various + Nexell parts such as S5Pxx18 series SoCs. All chips + have several I2C ports and all are provided, controlled by the + device tree. + config SYS_I2C_OMAP24XX bool "TI OMAP2+ I2C driver" depends on ARCH_OMAP2PLUS || ARCH_K3 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f5a471f..64b8ead 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o +obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o diff --git a/drivers/i2c/nx_i2c.c b/drivers/i2c/nx_i2c.c new file mode 100644 index 0000000..a3eec6c --- /dev/null +++ b/drivers/i2c/nx_i2c.c @@ -0,0 +1,537 @@ +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/clk.h> +#include <asm/arch/nx_gpio.h> + +#define I2C_WRITE 0 +#define I2C_READ 1 + +#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */ + +#define I2CLC_FILTER 0x04 /* SDA filter on*/ +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CSTAT_ABT 0x08 /* Arbitration bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRENB 0x20 /* Interrupt Enable bit */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ + +#define I2C_TIMEOUT_MS 10 /* 10 ms */ + +#define I2C_M_NOSTOP 0x100 + +#ifndef CONFIG_MAX_I2C_NUM +#define CONFIG_MAX_I2C_NUM 3 +#endif + +DECLARE_GLOBAL_DATA_PTR; + +struct nx_i2c_regs { + uint iiccon; + uint iicstat; + uint iicadd; + uint iicds; + uint iiclc; +}; + +struct nx_i2c_bus { + uint bus_num; + struct nx_i2c_regs *regs; + uint speed; + uint target_speed; + uint sda_delay; +}; + +/* s5pxx18 i2c must be reset before enabled */ +static void i2c_reset(int ch) +{ + int rst_id = RESET_ID_I2C0 + ch; + + nx_rstcon_setrst(rst_id, 0); + nx_rstcon_setrst(rst_id, 1); +} + +/* FIXME : this func will be removed after reset dm driver ported. + * set mmc pad alternative func. + */ +static void set_i2c_pad_func(struct nx_i2c_bus *i2c) +{ + switch (i2c->bus_num) { + case 0: + nx_gpio_set_pad_function(3, 2, 1); + nx_gpio_set_pad_function(3, 3, 1); + break; + case 1: + nx_gpio_set_pad_function(3, 4, 1); + nx_gpio_set_pad_function(3, 5, 1); + break; + case 2: + nx_gpio_set_pad_function(3, 6, 1); + nx_gpio_set_pad_function(3, 7, 1); + break; + } +} + +static uint i2c_get_clkrate(struct nx_i2c_bus *bus) +{ + struct clk *clk; + int index = bus->bus_num; + char name[50] = {0, }; + + sprintf(name, "%s.%d", DEV_NAME_I2C, index); + clk = clk_get((const char *)name); + if (!clk) + return -1; + + return clk_get_rate(clk); +} + +static uint i2c_set_clk(struct nx_i2c_bus *bus, uint enb) +{ + struct clk *clk; + char name[50]; + + sprintf(name, "%s.%d", DEV_NAME_I2C, bus->bus_num); + clk = clk_get((const char *)name); + if (!clk) + return -1; + + if (enb) { + clk_disable(clk); + clk_enable(clk); + } else { + clk_disable(clk); + } + + return 0; +} + +/* get i2c module number from base address */ +static uint i2c_get_busnum(struct nx_i2c_bus *bus) +{ + void *base_addr = (void *)PHY_BASEADDR_I2C0; + int i; + + for (i = 0; i < CONFIG_MAX_I2C_NUM; i++) { + if (base_addr == ((void *)bus->regs)) { + bus->bus_num = i; + return i; + } + base_addr += 0x1000; + } + + return -1; +} + +/* Set SDA line delay */ +static int nx_i2c_set_sda_delay(struct nx_i2c_bus *bus, ulong clkin) +{ + struct nx_i2c_regs *i2c = bus->regs; + uint sda_delay = 0; + + if (bus->sda_delay) { + sda_delay = clkin * bus->sda_delay; + sda_delay = DIV_ROUND_UP(sda_delay, 1000000); + sda_delay = DIV_ROUND_UP(sda_delay, 5); + if (sda_delay > 3) + sda_delay = 3; + sda_delay |= I2CLC_FILTER; + } else { + sda_delay = 0; + } + + sda_delay &= 0x7; + writel(sda_delay, &i2c->iiclc); + + return 0; +} + +/* Calculate the value of the divider and prescaler, set the bus speed. */ +static int nx_i2c_set_bus_speed(struct udevice *dev, uint speed) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + struct nx_i2c_regs *i2c = bus->regs; + unsigned long freq, pres = 16, div; + + freq = i2c_get_clkrate(bus); + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + writel((div & 0x0F) | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); + + /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(0x00, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); + + bus->speed = bus->target_speed / (div * pres); + + return 0; +} + +static void nx_i2c_set_clockrate(struct udevice *dev, uint speed) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + ulong clkin; + + nx_i2c_set_bus_speed(dev, speed); + clkin = bus->speed; /* the actual i2c speed */ + clkin /= 1000; /* clkin now in Khz */ + nx_i2c_set_sda_delay(bus, clkin); +} + +static void i2c_process_node(struct udevice *dev) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob; + + int node; + + node = dev->node.of_offset; + + bus->target_speed = fdtdec_get_int(blob, node, + "nexell,i2c-max-bus-freq", 0); + bus->sda_delay = fdtdec_get_int(blob, node, + "nexell,i2c-sda-delay", 0); +} + +static int nx_i2c_probe(struct udevice *dev) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + + /* get regs */ + bus->regs = (struct nx_i2c_regs *)devfdt_get_addr(dev); + /* calc index */ + if (!i2c_get_busnum(bus)) { + debug("not found i2c number!\n"); + return -1; + } + + /* i2c optional node parsing */ + i2c_process_node(dev); + if (!bus->target_speed) + return -1; + + /* reset */ + i2c_reset(bus->bus_num); + /* gpio pad */ + set_i2c_pad_func(bus); + + /* clock rate */ + i2c_set_clk(bus, 1); + nx_i2c_set_clockrate(dev, bus->target_speed); + i2c_set_clk(bus, 0); + + return 0; +} + +/* i2c bus busy check */ +static int i2c_is_busy(struct nx_i2c_regs *i2c) +{ + ulong start_time; + + start_time = get_timer(0); + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) { + debug("Timeout\n"); + return -I2C_NOK_TOUT; + } + } + return 0; +} + +/* irq enable/disable functions */ +static void i2c_enable_irq(struct nx_i2c_regs *i2c) +{ + unsigned int reg; + + reg = readl(&i2c->iiccon); + reg |= I2CCON_IRENB; + writel(reg, &i2c->iiccon); +} + +/* irq clear function */ +static void i2c_clear_irq(struct nx_i2c_regs *i2c) +{ + clrbits_le32(&i2c->iiccon, I2CCON_IRPND); +} + +/* ack enable functions */ +static void i2c_enable_ack(struct nx_i2c_regs *i2c) +{ + unsigned int reg; + + reg = readl(&i2c->iiccon); + reg |= I2CCON_ACKGEN; + writel(reg, &i2c->iiccon); +} + +static void i2c_send_stop(struct nx_i2c_regs *i2c) +{ + unsigned int reg; + + /* Send STOP. */ + reg = readl(&i2c->iicstat); + reg |= I2C_MODE_MR | I2C_TXRX_ENA; + reg &= (~I2C_START_STOP); + writel(reg, &i2c->iicstat); + i2c_clear_irq(i2c); +} + +static int wait_for_xfer(struct nx_i2c_regs *i2c) +{ + unsigned long start_time = get_timer(0); + + do { + if (readl(&i2c->iiccon) & I2CCON_IRPND) + return (readl(&i2c->iicstat) & I2CSTAT_NACK) ? + I2C_NACK : I2C_OK; + } while (get_timer(start_time) < I2C_TIMEOUT_MS); + + return I2C_NOK_TOUT; +} + +static int i2c_transfer(struct nx_i2c_regs *i2c, + uchar cmd_type, + uchar chip, + uchar addr[], + uchar addr_len, + uchar data[], + unsigned short data_len, + uint seq) +{ + uint status; + int i = 0, result; + + if (data == 0 || data_len == 0) { + /*Don't support data transfer of no length or to address 0 */ + debug("%s: bad call\n", __func__); + return I2C_NOK; + } + + i2c_enable_irq(i2c); + i2c_enable_ack(i2c); + + /* Get the slave chip address going */ + writel(chip, &i2c->iicds); + status = I2C_TXRX_ENA | I2C_START_STOP; + if (cmd_type == I2C_WRITE || (addr && addr_len)) + status |= I2C_MODE_MT; + else + status |= I2C_MODE_MR; + writel(status, &i2c->iicstat); + if (seq) + i2c_clear_irq(i2c); + + /* Wait for chip address to transmit. */ + result = wait_for_xfer(i2c); + if (result != I2C_OK) + goto bailout; + + /* If register address needs to be transmitted - do it now. */ + if (addr && addr_len) { /* register addr */ + while ((i < addr_len) && (result == I2C_OK)) { + writel(addr[i++], &i2c->iicds); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + } + + i = 0; + if (result != I2C_OK) + goto bailout; + } + + switch (cmd_type) { + case I2C_WRITE: + while ((i < data_len) && (result == I2C_OK)) { + writel(data[i++], &i2c->iicds); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + } + break; + case I2C_READ: + if (addr && addr_len) { + /* + * Register address has been sent, now send slave chip + * address again to start the actual read transaction. + */ + writel(chip, &i2c->iicds); + + /* Generate a re-START. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA + | I2C_START_STOP, &i2c->iicstat); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + if (result != I2C_OK) + goto bailout; + } + + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + clrbits_le32(&i2c->iiccon + , I2CCON_ACKGEN); + + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + data[i++] = readb(&i2c->iicds); + } + + if (result == I2C_NACK) + result = I2C_OK; /* Normal terminated read. */ + break; + + default: + debug("%s: bad call\n", __func__); + result = I2C_NOK; + break; + } + +bailout: + return result; +} + +static int nx_i2c_read(struct udevice *dev, uchar chip, uint addr, + uint alen, uchar *buffer, uint len, uint seq) +{ + struct nx_i2c_bus *i2c; + uchar xaddr[4]; + int ret; + + i2c = dev_get_priv(dev); + if (!i2c) + return -EFAULT; + + if (alen > 4) { + debug("I2C read: addr len %d not supported\n", alen); + return -EADDRNOTAVAIL; + } + + if (alen > 0) + xaddr[0] = (addr >> 24) & 0xFF; + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, + &xaddr[4 - alen], alen, buffer, len, seq); + + if (ret) { + debug("I2C read failed %d\n", ret); + return -EIO; + } + + return 0; +} + +static int nx_i2c_write(struct udevice *dev, uchar chip, uint addr, + uint alen, uchar *buffer, uint len, uint seq) +{ + struct nx_i2c_bus *i2c; + uchar xaddr[4]; + int ret; + + i2c = dev_get_priv(dev); + if (!i2c) + return -EFAULT; + + if (alen > 4) { + debug("I2C write: addr len %d not supported\n", alen); + return -EINVAL; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, + &xaddr[4 - alen], alen, buffer, len, seq); + if (ret) { + debug("I2C write failed %d\n", ret); + return -EIO; + } + + return 0; +} + +static int nx_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + struct nx_i2c_regs *i2c = bus->regs; + int ret; + int i; + + /* The power loss by the clock, only during on/off. */ + i2c_set_clk(bus, 1); + + /* Bus State(Busy) check */ + ret = i2c_is_busy(i2c); + if (ret < 0) + return ret; + + for (i = 0; i < nmsgs; msg++, i++) { + if (msg->flags & I2C_M_RD) { + ret = nx_i2c_read(dev, msg->addr, 0, 0, msg->buf, + msg->len, i); + } else { + ret = nx_i2c_write(dev, msg->addr, 0, 0, msg->buf, + msg->len, i); + } + + if (ret) { + debug("i2c_xfer: error sending\n"); + return -EREMOTEIO; + } + } + /* Send Stop */ + i2c_send_stop(i2c); + i2c_set_clk(bus, 0); + + return ret ? -EREMOTEIO : 0; +}; + +static const struct dm_i2c_ops nx_i2c_ops = { + .xfer = nx_i2c_xfer, + .set_bus_speed = nx_i2c_set_bus_speed, +}; + +static const struct udevice_id nx_i2c_ids[] = { + { .compatible = "nexell,s5pxx18-i2c" }, + { } +}; + +U_BOOT_DRIVER(i2c_nexell) = { + .name = "i2c_nexell", + .id = UCLASS_I2C, + .of_match = nx_i2c_ids, + .probe = nx_i2c_probe, + .priv_auto_alloc_size = sizeof(struct nx_i2c_bus), + .ops = &nx_i2c_ops, +}; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb8e7c0 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -253,6 +253,12 @@ config MMC_DW_SNPS This selects support for Synopsys DesignWare Memory Card Interface driver extensions used in various Synopsys ARC devboards.
+config NEXELL_DWMMC + bool "Nexell SD/MMC controller support" + depends on ARCH_NEXELL + depends on MMC_DW + default y + config MMC_MESON_GX bool "Meson GX EMMC controller support" depends on DM_MMC && BLK && ARCH_MESON diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..a7b5a7b 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o +obj-$(CONFIG_NEXELL_DWMMC) += nexell_dw_mmc_dm.o
# SDHCI obj-$(CONFIG_MMC_SDHCI) += sdhci.o diff --git a/drivers/mmc/nexell_dw_mmc_dm.c b/drivers/mmc/nexell_dw_mmc_dm.c new file mode 100644 index 0000000..b06b60d --- /dev/null +++ b/drivers/mmc/nexell_dw_mmc_dm.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Youngbok, Park park@nexell.co.kr + * + * (C) Copyright 2019 Stefan Bosch stefan_b@posteo.net + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-structs.h> +#include <dwmmc.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/reset.h> + +#define DWMCI_CLKSEL 0x09C +#define DWMCI_SHIFT_0 0x0 +#define DWMCI_SHIFT_1 0x1 +#define DWMCI_SHIFT_2 0x2 +#define DWMCI_SHIFT_3 0x3 +#define DWMCI_SET_SAMPLE_CLK(x) (x) +#define DWMCI_SET_DRV_CLK(x) ((x) << 16) +#define DWMCI_SET_DIV_RATIO(x) ((x) << 24) +#define DWMCI_CLKCTRL 0x114 +#define NX_MMC_CLK_DELAY(x, y, a, b) ((((x) & 0xFF) << 0) |\ + (((y) & 0x03) << 16) |\ + (((a) & 0xFF) << 8) |\ + (((b) & 0x03) << 24)) + +struct nexell_mmc_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +struct nexell_dwmmc_priv { + struct clk *clk; + struct dwmci_host host; + int fifo_size; + bool fifo_mode; + int frequency; + u32 min_freq; + u32 max_freq; + int d_delay; + int d_shift; + int s_delay; + int s_shift; + +}; + +struct clk *clk_get(const char *id); + +static void set_pin_stat(int index, int bit, int value) +{ +#if !defined(CONFIG_SPL_BUILD) + nx_gpio_set_pad_function(index, bit, value); +#else +#if defined(CONFIG_ARCH_S5P4418) || \ + defined(CONFIG_ARCH_S5P6818) + + unsigned long base[5] = { + PHY_BASEADDR_GPIOA, PHY_BASEADDR_GPIOB, + PHY_BASEADDR_GPIOC, PHY_BASEADDR_GPIOD, + PHY_BASEADDR_GPIOE, + }; + + dw_mmc_set_pin(base[index], bit, value); +#endif +#endif +} + +static void nx_dw_mmc_set_pin(struct dwmci_host *host) +{ + debug(" %s(): dev_index == %d", __func__, host->dev_index); + + switch (host->dev_index) { + case 0: + set_pin_stat(0, 29, 1); + set_pin_stat(0, 31, 1); + set_pin_stat(1, 1, 1); + set_pin_stat(1, 3, 1); + set_pin_stat(1, 5, 1); + set_pin_stat(1, 7, 1); + break; + case 1: + set_pin_stat(3, 22, 1); + set_pin_stat(3, 23, 1); + set_pin_stat(3, 24, 1); + set_pin_stat(3, 25, 1); + set_pin_stat(3, 26, 1); + set_pin_stat(3, 27, 1); + break; + case 2: + set_pin_stat(2, 18, 2); + set_pin_stat(2, 19, 2); + set_pin_stat(2, 20, 2); + set_pin_stat(2, 21, 2); + set_pin_stat(2, 22, 2); + set_pin_stat(2, 23, 2); + if (host->buswidth == 8) { + set_pin_stat(4, 21, 2); + set_pin_stat(4, 22, 2); + set_pin_stat(4, 23, 2); + set_pin_stat(4, 24, 2); + } + break; + default: + debug(" is invalid!"); + } + debug("\n"); +} + +static void nx_dw_mmc_clksel(struct dwmci_host *host) +{ + u32 val; + +#ifdef CONFIG_BOOST_MMC + val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | + DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(1); +#else + val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | + DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(3); +#endif + + dwmci_writel(host, DWMCI_CLKSEL, val); +} + +static void nx_dw_mmc_reset(int ch) +{ + int rst_id = RESET_ID_SDMMC0 + ch; + + nx_rstcon_setrst(rst_id, 0); + nx_rstcon_setrst(rst_id, 1); +} + +static void nx_dw_mmc_clk_delay(struct udevice *dev) +{ + unsigned int delay; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + + delay = NX_MMC_CLK_DELAY(priv->d_delay, + priv->d_shift, priv->s_delay, priv->s_shift); + + writel(delay, (host->ioaddr + DWMCI_CLKCTRL)); + debug("%s(): Values set: d_delay==%d, d_shift==%d, s_delay==%d, " + "s_shift==%d\n", __func__, priv->d_delay, priv->d_shift, + priv->s_delay, priv->s_shift); +} + +static unsigned int nx_dw_mmc_get_clk(struct dwmci_host *host, uint freq) +{ + struct clk *clk; + struct udevice *dev = host->priv; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + + int index = host->dev_index; + char name[50] = { 0, }; + + clk = priv->clk; + if (!clk) { + sprintf(name, "%s.%d", DEV_NAME_SDHC, index); + clk = clk_get((const char *)name); + if (!clk) + return 0; + priv->clk = clk; + } + + return clk_get_rate(clk) / 2; +} + +static unsigned long nx_dw_mmc_set_clk(struct dwmci_host *host, + unsigned int rate) +{ + struct clk *clk; + char name[50] = { 0, }; + struct udevice *dev = host->priv; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + + int index = host->dev_index; + + clk = priv->clk; + if (!clk) { + sprintf(name, "%s.%d", DEV_NAME_SDHC, index); + clk = clk_get((const char *)name); + if (!clk) + return 0; + priv->clk = clk; + } + + clk_disable(clk); + rate = clk_set_rate(clk, rate); + clk_enable(clk); + + return rate; +} + +static int nexell_dwmmc_ofdata_to_platdata(struct udevice *dev) +{ + /* if (dev): *priv = dev->priv, else: *priv = NULL */ + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + int val = -1; + + debug("%s()\n", __func__); + + host->name = dev->name; + host->ioaddr = dev_read_addr_ptr(dev); + + val = dev_read_u32_default(dev, "nexell,bus-width", -1); + if (val < 0) { + debug(" 'nexell,bus-width' missing/invalid!\n"); + return -EINVAL; + } + host->buswidth = val; + host->get_mmc_clk = nx_dw_mmc_get_clk; + host->clksel = nx_dw_mmc_clksel; + host->priv = dev; + + val = dev_read_u32_default(dev, "index", -1); + if (val < 0) { + debug(" 'index' missing/invalid!\n"); + return -EINVAL; + } + host->dev_index = val; + + val = dev_read_u32_default(dev, "fifo-size", 0x20); + if (val <= 0) { + debug(" 'fifo-size' missing/invalid!\n"); + return -EINVAL; + } + priv->fifo_size = val; + + priv->fifo_mode = dev_read_bool(dev, "fifo-mode"); + + val = dev_read_u32_default(dev, "frequency", -1); + if (val < 0) { + debug(" 'frequency' missing/invalid!\n"); + return -EINVAL; + } + priv->frequency = val; + + val = dev_read_u32_default(dev, "max-frequency", -1); + if (val < 0) { + debug(" 'max-frequency' missing/invalid!\n"); + return -EINVAL; + } + priv->max_freq = val; + priv->min_freq = 400000; /* 400 kHz */ + + val = dev_read_u32_default(dev, "nexell,drive_dly", -1); + if (val < 0) { + debug(" 'nexell,drive_dly' missing/invalid!\n"); + return -EINVAL; + } + priv->d_delay = val; + + val = dev_read_u32_default(dev, "nexell,drive_shift", -1); + if (val < 0) { + debug(" 'nexell,drive_shift' missing/invalid!\n"); + return -EINVAL; + } + priv->d_shift = val; + + val = dev_read_u32_default(dev, "nexell,sample_dly", -1); + if (val < 0) { + debug(" 'nexell,sample_dly' missing/invalid!\n"); + return -EINVAL; + } + priv->s_delay = val; + + val = dev_read_u32_default(dev, "nexell,sample_shift", -1); + if (val < 0) { + debug(" 'nexell,sample_shift' missing/invalid!\n"); + return -EINVAL; + } + priv->s_shift = val; + + debug(" index==%d, name==%s, ioaddr==0x%08x, buswidth==%d, " + "fifo_size==%d, fifo_mode==%d, frequency==%d\n", + host->dev_index, host->name, (u32)host->ioaddr, + host->buswidth, priv->fifo_size, priv->fifo_mode, + priv->frequency); + debug(" min_freq==%d, max_freq==%d, delay: " + "0x%02x:0x%02x:0x%02x:0x%02x\n", + priv->min_freq, priv->max_freq, priv->d_delay, + priv->d_shift, priv->s_delay, priv->s_shift); + + return 0; +} + +static int nexell_dwmmc_probe(struct udevice *dev) +{ + struct nexell_mmc_plat *plat = dev_get_platdata(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + struct udevice *pwr_dev __maybe_unused; + + debug("%s():\n", __func__); + + host->fifoth_val = MSIZE(0x2) | + RX_WMARK(priv->fifo_size / 2 - 1) | + TX_WMARK(priv->fifo_size / 2); + + host->fifo_mode = priv->fifo_mode; + + dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq); + host->mmc = &plat->mmc; + host->mmc->priv = &priv->host; + host->mmc->dev = dev; + upriv->mmc = host->mmc; + + nx_dw_mmc_set_pin(host); + + debug(" nx_dw_mmc_set_clk(host, frequency * 4 == %d)\n", + priv->frequency * 4); + nx_dw_mmc_set_clk(host, priv->frequency * 4); + + nx_dw_mmc_reset(host->dev_index); + nx_dw_mmc_clk_delay(dev); + + return dwmci_probe(dev); +} + +static int nexell_dwmmc_bind(struct udevice *dev) +{ + struct nexell_mmc_plat *plat = dev_get_platdata(dev); + + return dwmci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id nexell_dwmmc_ids[] = { + { .compatible = "nexell,nexell-dwmmc" }, + { } +}; + +U_BOOT_DRIVER(nexell_dwmmc_drv) = { + .name = "nexell_dwmmc", + .id = UCLASS_MMC, + .of_match = nexell_dwmmc_ids, + .ofdata_to_platdata = nexell_dwmmc_ofdata_to_platdata, + .ops = &dm_dwmci_ops, + .bind = nexell_dwmmc_bind, + .probe = nexell_dwmmc_probe, + .priv_auto_alloc_size = sizeof(struct nexell_dwmmc_priv), + .platdata_auto_alloc_size = sizeof(struct nexell_mmc_plat), +}; diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a837c35..b45aada 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o +obj-$(CONFIG_PWM_NX) += pwm-nexell.o diff --git a/drivers/pwm/pwm-nexell.c b/drivers/pwm/pwm-nexell.c new file mode 100644 index 0000000..6c0f8f4 --- /dev/null +++ b/drivers/pwm/pwm-nexell.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 Samsung Electronics + * + * Donghwa Lee dh09.lee@samsung.com + */ + +/* This codes are copied from arch/arm/cpu/armv7/s5p-common/pwm.c */ + +#include <common.h> +#include <errno.h> +#include <pwm.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include "pwm-nexell.h" + +#if defined(CONFIG_ARCH_NEXELL) +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/tieoff.h> + +struct pwm_device { + int ch; + int grp; + int bit; + int pwm_fn; +}; + +static struct pwm_device pwm_dev[] = { + [0] = { .ch = 0, .grp = 3, .bit = 1, .pwm_fn = 1 }, + [1] = { .ch = 1, .grp = 2, .bit = 13, .pwm_fn = 2 }, + [2] = { .ch = 2, .grp = 2, .bit = 14, .pwm_fn = 2 }, + [3] = { .ch = 3, .grp = 3, .bit = 0, .pwm_fn = 2 }, +}; +#endif + +int pwm_enable(int pwm_id) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long tcon; + + tcon = readl(&pwm->tcon); + tcon |= TCON_START(pwm_id); + + writel(tcon, &pwm->tcon); + + return 0; +} + +void pwm_disable(int pwm_id) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long tcon; + + tcon = readl(&pwm->tcon); + tcon &= ~TCON_START(pwm_id); + + writel(tcon, &pwm->tcon); +} + +static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq) +{ + unsigned long tin_parent_rate; + unsigned int div; +#if defined(CONFIG_ARCH_NEXELL) + unsigned int pre_div; + const struct s5p_timer *pwm = + (struct s5p_timer *)PHY_BASEADDR_PWM; + unsigned int val; +#endif + +#if defined(CONFIG_ARCH_NEXELL) + struct clk *clk = clk_get(CORECLK_NAME_PCLK); + + tin_parent_rate = clk_get_rate(clk); +#else + tin_parent_rate = get_pwm_clk(); +#endif + +#if defined(CONFIG_ARCH_NEXELL) + writel(0, &pwm->tcfg0); + val = readl(&pwm->tcfg0); + + if (pwm_id < 2) + div = ((val >> 0) & 0xff) + 1; + else + div = ((val >> 8) & 0xff) + 1; + + writel(0, &pwm->tcfg1); + val = readl(&pwm->tcfg1); + val = (val >> MUX_DIV_SHIFT(pwm_id)) & 0xF; + pre_div = (1UL << val); + + freq = tin_parent_rate / div / pre_div; + + return freq; +#else + for (div = 2; div <= 16; div *= 2) { + if ((tin_parent_rate / (div << 16)) < freq) + return tin_parent_rate / div; + } + + return tin_parent_rate / 16; +#endif +} + +#define NS_IN_SEC 1000000000UL + +int pwm_config(int pwm_id, int duty_ns, int period_ns) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned int offset; + unsigned long tin_rate; + unsigned long tin_ns; + unsigned long frequency; + unsigned long tcon; + unsigned long tcnt; + unsigned long tcmp; + + /* + * We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1GHz is easily representable + * by 32bits. + */ + if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0) + return -ERANGE; + + if (duty_ns > period_ns) + return -EINVAL; + + frequency = NS_IN_SEC / period_ns; + + /* Check to see if we are changing the clock rate of the PWM */ + tin_rate = pwm_calc_tin(pwm_id, frequency); + + tin_ns = NS_IN_SEC / tin_rate; +#if defined(CONFIG_ARCH_NEXELL) + /* The counter starts at zero. */ + tcnt = (period_ns / tin_ns) - 1; +#else + tcnt = period_ns / tin_ns; +#endif + + /* Note, counters count down */ + tcmp = duty_ns / tin_ns; + tcmp = tcnt - tcmp; + + /* Update the PWM register block. */ + offset = pwm_id * 3; + if (pwm_id < 4) { + writel(tcnt, &pwm->tcntb0 + offset); + writel(tcmp, &pwm->tcmpb0 + offset); + } + + tcon = readl(&pwm->tcon); + tcon |= TCON_UPDATE(pwm_id); + if (pwm_id < 4) + tcon |= TCON_AUTO_RELOAD(pwm_id); + else + tcon |= TCON4_AUTO_RELOAD; + writel(tcon, &pwm->tcon); + + tcon &= ~TCON_UPDATE(pwm_id); + writel(tcon, &pwm->tcon); + + return 0; +} + +int pwm_init(int pwm_id, int div, int invert) +{ + u32 val; + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long ticks_per_period; + unsigned int offset, prescaler; + + /* + * Timer Freq(HZ) = + * PWM_CLK / { (prescaler_value + 1) * (divider_value) } + */ + + val = readl(&pwm->tcfg0); + if (pwm_id < 2) { + prescaler = PRESCALER_0; + val &= ~0xff; + val |= (prescaler & 0xff); + } else { + prescaler = PRESCALER_1; + val &= ~(0xff << 8); + val |= (prescaler & 0xff) << 8; + } + writel(val, &pwm->tcfg0); + val = readl(&pwm->tcfg1); + val &= ~(0xf << MUX_DIV_SHIFT(pwm_id)); + val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id); + writel(val, &pwm->tcfg1); + + if (pwm_id == 4) { + /* + * TODO(sjg): Use this as a countdown timer for now. We count + * down from the maximum value to 0, then reset. + */ + ticks_per_period = -1UL; + } else { + const unsigned long pwm_hz = 1000; +#if defined(CONFIG_ARCH_NEXELL) + struct clk *clk = clk_get(CORECLK_NAME_PCLK); + unsigned long timer_rate_hz = clk_get_rate(clk) / +#else + unsigned long timer_rate_hz = get_pwm_clk() / +#endif + ((prescaler + 1) * (1 << div)); + + ticks_per_period = timer_rate_hz / pwm_hz; + } + + /* set count value */ + offset = pwm_id * 3; + + writel(ticks_per_period, &pwm->tcntb0 + offset); + + val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id)); + if (invert && pwm_id < 4) + val |= TCON_INVERTER(pwm_id); + writel(val, &pwm->tcon); + + nx_gpio_set_pad_function(pwm_dev[pwm_id].grp, pwm_dev[pwm_id].bit, + pwm_dev[pwm_id].pwm_fn); + pwm_enable(pwm_id); + + return 0; +} diff --git a/drivers/pwm/pwm-nexell.h b/drivers/pwm/pwm-nexell.h new file mode 100644 index 0000000..92dc707 --- /dev/null +++ b/drivers/pwm/pwm-nexell.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2009 Samsung Electronics + * Kyungmin Park kyungmin.park@samsung.com + * Minkyu Kang mk7.kang@samsung.com + */ + +#ifndef __ASM_ARM_ARCH_PWM_H_ +#define __ASM_ARM_ARCH_PWM_H_ + +#define PRESCALER_0 (8 - 1) /* prescaler of timer 0, 1 */ +#define PRESCALER_1 (16 - 1) /* prescaler of timer 2, 3, 4 */ + +/* Divider MUX */ +#define MUX_DIV_1 0 /* 1/1 period */ +#define MUX_DIV_2 1 /* 1/2 period */ +#define MUX_DIV_4 2 /* 1/4 period */ +#define MUX_DIV_8 3 /* 1/8 period */ +#define MUX_DIV_16 4 /* 1/16 period */ + +#define MUX_DIV_SHIFT(x) ((x) * 4) + +#define TCON_OFFSET(x) (((x) + 1) * (!!x) << 2) + +#define TCON_START(x) (1 << TCON_OFFSET(x)) +#define TCON_UPDATE(x) (1 << (TCON_OFFSET(x) + 1)) +#define TCON_INVERTER(x) (1 << (TCON_OFFSET(x) + 2)) +#define TCON_AUTO_RELOAD(x) (1 << (TCON_OFFSET(x) + 3)) +#define TCON4_AUTO_RELOAD (1 << 22) + +#ifndef __ASSEMBLY__ +struct s5p_timer { + unsigned int tcfg0; + unsigned int tcfg1; + unsigned int tcon; + unsigned int tcntb0; + unsigned int tcmpb0; + unsigned int tcnto0; + unsigned int tcntb1; + unsigned int tcmpb1; + unsigned int tcnto1; + unsigned int tcntb2; + unsigned int tcmpb2; + unsigned int tcnto2; + unsigned int tcntb3; + unsigned int res1; + unsigned int tcnto3; + unsigned int tcntb4; + unsigned int tcnto4; + unsigned int tintcstat; +}; +#endif /* __ASSEMBLY__ */ + +#endif

Hello Stefan,
Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- i2c/nx_i2c.c: Some adaptions mainly because of changes in "struct udevice".
- mmc: nexell_dw_mmc.c changed to nexell_dw_mmc_dm.c (switched to DM).
Signed-off-by: Stefan Bosch stefan_b@posteo.net
drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/nx_gpio.c | 252 +++++++++++++++++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/nx_i2c.c | 537 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/nexell_dw_mmc_dm.c | 350 +++++++++++++++++++++++++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-nexell.c | 252 +++++++++++++++++++ drivers/pwm/pwm-nexell.h | 54 +++++
Could you please split this patch into 4 parts (i2c, gpio, mmc and pwm) ?
Thanks!
12 files changed, 1473 insertions(+) create mode 100644 drivers/gpio/nx_gpio.c create mode 100644 drivers/i2c/nx_i2c.c create mode 100644 drivers/mmc/nexell_dw_mmc_dm.c create mode 100644 drivers/pwm/pwm-nexell.c create mode 100644 drivers/pwm/pwm-nexell.h
[...]
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 449046b..e3340de 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o +obj-$(CONFIG_NX_GPIO) += nx_gpio.o
Please keep lists sorted.
diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c new file mode 100644 index 0000000..86472f6 --- /dev/null +++ b/drivers/gpio/nx_gpio.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- DeokJin, Lee truevirtue@nexell.co.kr
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/gpio.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_gpio_regs {
- u32 data; /* Data register */
- u32 outputenb; /* Output Enable register */
- u32 detmode[2]; /* Detect Mode Register */
- u32 intenb; /* Interrupt Enable Register */
- u32 det; /* Event Detect Register */
- u32 pad; /* Pad Status Register */
+};
+struct nx_alive_gpio_regs {
- u32 pwrgate; /* Power Gating Register */
- u32 reserved0[28]; /* Reserved0 */
- u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */
- u32 outputenb; /* Alive GPIO Output Enable Register */
- u32 outputenb_read; /* Alive GPIO Output Read Register */
- u32 reserved1[3]; /* Reserved1 */
- u32 pad_reset; /* Alive GPIO Output Reset Register */
- u32 data; /* Alive GPIO Output Register */
- u32 pad_read; /* Alive GPIO Pad Read Register */
- u32 reserved2[33]; /* Reserved2 */
- u32 pad; /* Alive GPIO Input Value Register */
+};
+struct nx_gpio_platdata {
- void *regs;
- int gpio_count;
- const char *bank_name;
+};
+static int nx_alive_gpio_is_check(struct udevice *dev) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- const char *bank_name = plat->bank_name;
- if (!strcmp(bank_name, "gpio_alv"))
return 1;
- return 0;
+}
+static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned int pin) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_alive_gpio_regs *const regs = plat->regs;
- setbits_le32(®s->outputenb_reset, 1 << pin);
- return 0;
+}
+static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned int pin,
int val)
+{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_alive_gpio_regs *const regs = plat->regs;
- if (val)
setbits_le32(®s->data, 1 << pin);
- else
setbits_le32(®s->pad_reset, 1 << pin);
- setbits_le32(®s->outputenb, 1 << pin);
- return 0;
+}
+static int nx_alive_gpio_get_value(struct udevice *dev, unsigned int pin) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_alive_gpio_regs *const regs = plat->regs;
- unsigned int mask = 1UL << pin;
- unsigned int value;
- value = (readl(®s->pad_read) & mask) >> pin;
- return value;
+}
+static int nx_alive_gpio_set_value(struct udevice *dev, unsigned int pin,
int val)
+{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_alive_gpio_regs *const regs = plat->regs;
- if (val)
setbits_le32(®s->data, 1 << pin);
- else
clrbits_le32(®s->pad_reset, 1 << pin);
- return 0;
+}
+static int nx_alive_gpio_get_function(struct udevice *dev, unsigned int pin) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_alive_gpio_regs *const regs = plat->regs;
- unsigned int mask = (1UL << pin);
- unsigned int output;
- output = readl(®s->outputenb_read) & mask;
- if (output)
return GPIOF_OUTPUT;
- else
return GPIOF_INPUT;
+}
+static int nx_gpio_direction_input(struct udevice *dev, unsigned int pin) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_gpio_regs *const regs = plat->regs;
- if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_direction_input(dev, pin);
- clrbits_le32(®s->outputenb, 1 << pin);
- return 0;
+}
+static int nx_gpio_direction_output(struct udevice *dev, unsigned int pin,
int val)
+{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_gpio_regs *const regs = plat->regs;
- if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_direction_output(dev, pin, val);
- if (val)
setbits_le32(®s->data, 1 << pin);
- else
clrbits_le32(®s->data, 1 << pin);
- setbits_le32(®s->outputenb, 1 << pin);
- return 0;
+}
+static int nx_gpio_get_value(struct udevice *dev, unsigned int pin) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_gpio_regs *const regs = plat->regs;
- unsigned int mask = 1UL << pin;
- unsigned int value;
- if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_get_value(dev, pin);
- value = (readl(®s->pad) & mask) >> pin;
- return value;
+}
+static int nx_gpio_set_value(struct udevice *dev, unsigned int pin, int val) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_gpio_regs *const regs = plat->regs;
- if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_set_value(dev, pin, val);
- if (val)
setbits_le32(®s->data, 1 << pin);
- else
clrbits_le32(®s->data, 1 << pin);
- return 0;
+}
+static int nx_gpio_get_function(struct udevice *dev, unsigned int pin) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_gpio_regs *const regs = plat->regs;
- unsigned int mask = (1UL << pin);
- unsigned int output;
- if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_get_function(dev, pin);
- output = readl(®s->outputenb) & mask;
- if (output)
return GPIOF_OUTPUT;
- else
return GPIOF_INPUT;
+}
+static int nx_gpio_probe(struct udevice *dev) +{
- struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- uc_priv->gpio_count = plat->gpio_count;
- uc_priv->bank_name = plat->bank_name;
- return 0;
+}
+static int nx_gpio_ofdata_to_platdata(struct udevice *dev) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- plat->regs = map_physmem(devfdt_get_addr(dev),
sizeof(struct nx_gpio_regs),
MAP_NOCACHE);
- plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->node.of_offset,
"nexell,gpio-bank-width", 32);
- plat->bank_name = fdt_getprop(gd->fdt_blob, dev->node.of_offset,
"gpio-bank-name", NULL);
- return 0;
+}
+static const struct dm_gpio_ops nx_gpio_ops = {
- .direction_input = nx_gpio_direction_input,
- .direction_output = nx_gpio_direction_output,
- .get_value = nx_gpio_get_value,
- .set_value = nx_gpio_set_value,
- .get_function = nx_gpio_get_function,
+};
+static const struct udevice_id nx_gpio_ids[] = {
- { .compatible = "nexell,nexell-gpio" },
- { }
+};
+U_BOOT_DRIVER(nx_gpio) = {
- .name = "nx_gpio",
- .id = UCLASS_GPIO,
- .of_match = nx_gpio_ids,
- .ops = &nx_gpio_ops,
- .ofdata_to_platdata = nx_gpio_ofdata_to_platdata,
- .platdata_auto_alloc_size = sizeof(struct nx_gpio_platdata),
- .probe = nx_gpio_probe,
+}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 03d2fed..2cd0ed3 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -317,6 +317,15 @@ config SYS_MXC_I2C8_SLAVE MXC I2C8 Slave endif
+config SYS_I2C_NEXELL
- bool "Nexell I2C driver"
- depends on DM_I2C
- help
Add support for the Nexell I2C driver. This is used with various
Nexell parts such as S5Pxx18 series SoCs. All chips
have several I2C ports and all are provided, controlled by the
device tree.
- config SYS_I2C_OMAP24XX bool "TI OMAP2+ I2C driver" depends on ARCH_OMAP2PLUS || ARCH_K3
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f5a471f..64b8ead 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o +obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o diff --git a/drivers/i2c/nx_i2c.c b/drivers/i2c/nx_i2c.c new file mode 100644 index 0000000..a3eec6c --- /dev/null +++ b/drivers/i2c/nx_i2c.c @@ -0,0 +1,537 @@ +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/clk.h> +#include <asm/arch/nx_gpio.h>
+#define I2C_WRITE 0 +#define I2C_READ 1
+#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */
+#define I2CLC_FILTER 0x04 /* SDA filter on*/ +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CSTAT_ABT 0x08 /* Arbitration bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRENB 0x20 /* Interrupt Enable bit */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
+#define I2C_TIMEOUT_MS 10 /* 10 ms */
+#define I2C_M_NOSTOP 0x100
+#ifndef CONFIG_MAX_I2C_NUM +#define CONFIG_MAX_I2C_NUM 3 +#endif
Is this really configurable? If so, I do not find the Kconfig description.
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_i2c_regs {
- uint iiccon;
- uint iicstat;
- uint iicadd;
- uint iicds;
- uint iiclc;
+};
+struct nx_i2c_bus {
- uint bus_num;
- struct nx_i2c_regs *regs;
- uint speed;
- uint target_speed;
- uint sda_delay;
+};
+/* s5pxx18 i2c must be reset before enabled */ +static void i2c_reset(int ch) +{
- int rst_id = RESET_ID_I2C0 + ch;
- nx_rstcon_setrst(rst_id, 0);
- nx_rstcon_setrst(rst_id, 1);
+}
+/* FIXME : this func will be removed after reset dm driver ported.
- set mmc pad alternative func.
- */
+static void set_i2c_pad_func(struct nx_i2c_bus *i2c) +{
- switch (i2c->bus_num) {
- case 0:
nx_gpio_set_pad_function(3, 2, 1);
nx_gpio_set_pad_function(3, 3, 1);
break;
- case 1:
nx_gpio_set_pad_function(3, 4, 1);
nx_gpio_set_pad_function(3, 5, 1);
break;
- case 2:
nx_gpio_set_pad_function(3, 6, 1);
nx_gpio_set_pad_function(3, 7, 1);
break;
- }
+}
Hmm... may this should be moved into a seperate pincontrol driver?
+static uint i2c_get_clkrate(struct nx_i2c_bus *bus) +{
- struct clk *clk;
- int index = bus->bus_num;
- char name[50] = {0, };
?
- sprintf(name, "%s.%d", DEV_NAME_I2C, index);
Where is DEV_NAME_I2C defined ?
- clk = clk_get((const char *)name);
- if (!clk)
return -1;
- return clk_get_rate(clk);
+}
+static uint i2c_set_clk(struct nx_i2c_bus *bus, uint enb) +{
- struct clk *clk;
- char name[50];
- sprintf(name, "%s.%d", DEV_NAME_I2C, bus->bus_num);
- clk = clk_get((const char *)name);
- if (!clk)
return -1;
- if (enb) {
clk_disable(clk);
clk_enable(clk);
- } else {
clk_disable(clk);
- }
- return 0;
+}
+/* get i2c module number from base address */ +static uint i2c_get_busnum(struct nx_i2c_bus *bus) +{
- void *base_addr = (void *)PHY_BASEADDR_I2C0;
- int i;
- for (i = 0; i < CONFIG_MAX_I2C_NUM; i++) {
if (base_addr == ((void *)bus->regs)) {
bus->bus_num = i;
return i;
}
base_addr += 0x1000;
- }
- return -1;
return -ENODEV;
Hmm... is there no chance to use seq from struct udevice
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/dm/device.h#L152
?
For example like: https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/mxc_i2c.c#L895
+}
+/* Set SDA line delay */ +static int nx_i2c_set_sda_delay(struct nx_i2c_bus *bus, ulong clkin) +{
- struct nx_i2c_regs *i2c = bus->regs;
- uint sda_delay = 0;
- if (bus->sda_delay) {
sda_delay = clkin * bus->sda_delay;
sda_delay = DIV_ROUND_UP(sda_delay, 1000000);
sda_delay = DIV_ROUND_UP(sda_delay, 5);
if (sda_delay > 3)
sda_delay = 3;
sda_delay |= I2CLC_FILTER;
- } else {
sda_delay = 0;
- }
- sda_delay &= 0x7;
- writel(sda_delay, &i2c->iiclc);
- return 0;
+}
+/* Calculate the value of the divider and prescaler, set the bus speed. */ +static int nx_i2c_set_bus_speed(struct udevice *dev, uint speed) +{
- struct nx_i2c_bus *bus = dev_get_priv(dev);
- struct nx_i2c_regs *i2c = bus->regs;
- unsigned long freq, pres = 16, div;
- freq = i2c_get_clkrate(bus);
- /* calculate prescaler and divisor values */
- if ((freq / pres / (16 + 1)) > speed)
/* set prescaler to 512 */
pres = 512;
- div = 0;
- while ((freq / pres / (div + 1)) > speed)
div++;
- /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */
- writel((div & 0x0F) | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
- /* init to SLAVE REVEIVE and set slaveaddr */
- writel(0, &i2c->iicstat);
- writel(0x00, &i2c->iicadd);
- /* program Master Transmit (and implicit STOP) */
- writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
- bus->speed = bus->target_speed / (div * pres);
Do you want to allow all values of speeds or may you want to use standard speeds, see:
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L33
- return 0;
+}
+static void nx_i2c_set_clockrate(struct udevice *dev, uint speed) +{
- struct nx_i2c_bus *bus = dev_get_priv(dev);
- ulong clkin;
- nx_i2c_set_bus_speed(dev, speed);
- clkin = bus->speed; /* the actual i2c speed */
- clkin /= 1000; /* clkin now in Khz */
- nx_i2c_set_sda_delay(bus, clkin);
+}
+static void i2c_process_node(struct udevice *dev) +{
- struct nx_i2c_bus *bus = dev_get_priv(dev);
- const void *blob = gd->fdt_blob;
- int node;
- node = dev->node.of_offset;
- bus->target_speed = fdtdec_get_int(blob, node,
"nexell,i2c-max-bus-freq", 0);
- bus->sda_delay = fdtdec_get_int(blob, node,
"nexell,i2c-sda-delay", 0);
You introdue here new properties, please document them in u-boot:/doc/device-tree-bindings/i2c
Please without "nexell,"
Do you plan to post also a linux i2c driver?
If so, the devicetree bindings should be discussed there to avoid different properties between U-Boot and linux!
+}
+static int nx_i2c_probe(struct udevice *dev) +{
- struct nx_i2c_bus *bus = dev_get_priv(dev);
- /* get regs */
- bus->regs = (struct nx_i2c_regs *)devfdt_get_addr(dev);
- /* calc index */
- if (!i2c_get_busnum(bus)) {
debug("not found i2c number!\n");
return -1;
please return -ENODEV
- }
- /* i2c optional node parsing */
- i2c_process_node(dev);
- if (!bus->target_speed)
return -1;
please return here also an errorcode from include/linux/errno.h
Hmm.. if you return here if target_speed is not set, it is not optional!
- /* reset */
- i2c_reset(bus->bus_num);
- /* gpio pad */
- set_i2c_pad_func(bus);
- /* clock rate */
- i2c_set_clk(bus, 1);
- nx_i2c_set_clockrate(dev, bus->target_speed);
- i2c_set_clk(bus, 0);
- return 0;
+}
+/* i2c bus busy check */ +static int i2c_is_busy(struct nx_i2c_regs *i2c) +{
- ulong start_time;
- start_time = get_timer(0);
- while (readl(&i2c->iicstat) & I2CSTAT_BSY) {
if (get_timer(start_time) > I2C_TIMEOUT_MS) {
debug("Timeout\n");
return -I2C_NOK_TOUT;
}
- }
- return 0;
+}
+/* irq enable/disable functions */ +static void i2c_enable_irq(struct nx_i2c_regs *i2c) +{
- unsigned int reg;
- reg = readl(&i2c->iiccon);
- reg |= I2CCON_IRENB;
- writel(reg, &i2c->iiccon);
+}
+/* irq clear function */ +static void i2c_clear_irq(struct nx_i2c_regs *i2c) +{
- clrbits_le32(&i2c->iiccon, I2CCON_IRPND);
+}
+/* ack enable functions */ +static void i2c_enable_ack(struct nx_i2c_regs *i2c) +{
- unsigned int reg;
- reg = readl(&i2c->iiccon);
- reg |= I2CCON_ACKGEN;
- writel(reg, &i2c->iiccon);
+}
+static void i2c_send_stop(struct nx_i2c_regs *i2c) +{
- unsigned int reg;
- /* Send STOP. */
- reg = readl(&i2c->iicstat);
- reg |= I2C_MODE_MR | I2C_TXRX_ENA;
- reg &= (~I2C_START_STOP);
- writel(reg, &i2c->iicstat);
- i2c_clear_irq(i2c);
+}
+static int wait_for_xfer(struct nx_i2c_regs *i2c) +{
- unsigned long start_time = get_timer(0);
- do {
if (readl(&i2c->iiccon) & I2CCON_IRPND)
return (readl(&i2c->iicstat) & I2CSTAT_NACK) ?
I2C_NACK : I2C_OK;
- } while (get_timer(start_time) < I2C_TIMEOUT_MS);
- return I2C_NOK_TOUT;
+}
+static int i2c_transfer(struct nx_i2c_regs *i2c,
uchar cmd_type,
uchar chip,
uchar addr[],
uchar addr_len,
uchar data[],
unsigned short data_len,
uint seq)
+{
- uint status;
- int i = 0, result;
- if (data == 0 || data_len == 0) {
/*Don't support data transfer of no length or to address 0 */
debug("%s: bad call\n", __func__);
return I2C_NOK;
- }
- i2c_enable_irq(i2c);
- i2c_enable_ack(i2c);
- /* Get the slave chip address going */
- writel(chip, &i2c->iicds);
- status = I2C_TXRX_ENA | I2C_START_STOP;
- if (cmd_type == I2C_WRITE || (addr && addr_len))
status |= I2C_MODE_MT;
- else
status |= I2C_MODE_MR;
- writel(status, &i2c->iicstat);
- if (seq)
i2c_clear_irq(i2c);
- /* Wait for chip address to transmit. */
- result = wait_for_xfer(i2c);
- if (result != I2C_OK)
goto bailout;
- /* If register address needs to be transmitted - do it now. */
- if (addr && addr_len) { /* register addr */
while ((i < addr_len) && (result == I2C_OK)) {
writel(addr[i++], &i2c->iicds);
i2c_clear_irq(i2c);
result = wait_for_xfer(i2c);
}
i = 0;
if (result != I2C_OK)
goto bailout;
- }
- switch (cmd_type) {
- case I2C_WRITE:
while ((i < data_len) && (result == I2C_OK)) {
writel(data[i++], &i2c->iicds);
i2c_clear_irq(i2c);
result = wait_for_xfer(i2c);
}
break;
- case I2C_READ:
if (addr && addr_len) {
/*
* Register address has been sent, now send slave chip
* address again to start the actual read transaction.
*/
writel(chip, &i2c->iicds);
/* Generate a re-START. */
writel(I2C_MODE_MR | I2C_TXRX_ENA
| I2C_START_STOP, &i2c->iicstat);
i2c_clear_irq(i2c);
result = wait_for_xfer(i2c);
if (result != I2C_OK)
goto bailout;
}
while ((i < data_len) && (result == I2C_OK)) {
/* disable ACK for final READ */
if (i == data_len - 1)
clrbits_le32(&i2c->iiccon
, I2CCON_ACKGEN);
i2c_clear_irq(i2c);
result = wait_for_xfer(i2c);
data[i++] = readb(&i2c->iicds);
}
if (result == I2C_NACK)
result = I2C_OK; /* Normal terminated read. */
break;
- default:
debug("%s: bad call\n", __func__);
result = I2C_NOK;
break;
- }
+bailout:
- return result;
+}
+static int nx_i2c_read(struct udevice *dev, uchar chip, uint addr,
uint alen, uchar *buffer, uint len, uint seq)
+{
- struct nx_i2c_bus *i2c;
- uchar xaddr[4];
- int ret;
- i2c = dev_get_priv(dev);
- if (!i2c)
return -EFAULT;
- if (alen > 4) {
debug("I2C read: addr len %d not supported\n", alen);
return -EADDRNOTAVAIL;
- }
- if (alen > 0)
xaddr[0] = (addr >> 24) & 0xFF;
- if (alen > 0) {
xaddr[0] = (addr >> 24) & 0xFF;
xaddr[1] = (addr >> 16) & 0xFF;
xaddr[2] = (addr >> 8) & 0xFF;
xaddr[3] = addr & 0xFF;
- }
- ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1,
&xaddr[4 - alen], alen, buffer, len, seq);
- if (ret) {
debug("I2C read failed %d\n", ret);
return -EIO;
- }
- return 0;
+}
+static int nx_i2c_write(struct udevice *dev, uchar chip, uint addr,
uint alen, uchar *buffer, uint len, uint seq)
+{
- struct nx_i2c_bus *i2c;
- uchar xaddr[4];
- int ret;
- i2c = dev_get_priv(dev);
- if (!i2c)
return -EFAULT;
- if (alen > 4) {
debug("I2C write: addr len %d not supported\n", alen);
return -EINVAL;
- }
- if (alen > 0) {
xaddr[0] = (addr >> 24) & 0xFF;
xaddr[1] = (addr >> 16) & 0xFF;
xaddr[2] = (addr >> 8) & 0xFF;
xaddr[3] = addr & 0xFF;
- }
- ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1,
&xaddr[4 - alen], alen, buffer, len, seq);
- if (ret) {
debug("I2C write failed %d\n", ret);
return -EIO;
- }
- return 0;
+}
+static int nx_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{
- struct nx_i2c_bus *bus = dev_get_priv(dev);
- struct nx_i2c_regs *i2c = bus->regs;
- int ret;
- int i;
- /* The power loss by the clock, only during on/off. */
- i2c_set_clk(bus, 1);
- /* Bus State(Busy) check */
- ret = i2c_is_busy(i2c);
- if (ret < 0)
return ret;
- for (i = 0; i < nmsgs; msg++, i++) {
if (msg->flags & I2C_M_RD) {
ret = nx_i2c_read(dev, msg->addr, 0, 0, msg->buf,
msg->len, i);
} else {
ret = nx_i2c_write(dev, msg->addr, 0, 0, msg->buf,
msg->len, i);
}
if (ret) {
debug("i2c_xfer: error sending\n");
return -EREMOTEIO;
}
- }
- /* Send Stop */
- i2c_send_stop(i2c);
- i2c_set_clk(bus, 0);
- return ret ? -EREMOTEIO : 0;
+};
+static const struct dm_i2c_ops nx_i2c_ops = {
- .xfer = nx_i2c_xfer,
- .set_bus_speed = nx_i2c_set_bus_speed,
+};
+static const struct udevice_id nx_i2c_ids[] = {
- { .compatible = "nexell,s5pxx18-i2c" },
Same here as for the new properties. Please discuss the names on
devicetree@vger.kernel.org devicetree@vger.kernel.org
first!
- { }
+};
+U_BOOT_DRIVER(i2c_nexell) = {
- .name = "i2c_nexell",
- .id = UCLASS_I2C,
- .of_match = nx_i2c_ids,
- .probe = nx_i2c_probe,
- .priv_auto_alloc_size = sizeof(struct nx_i2c_bus),
- .ops = &nx_i2c_ops,
+};
bye, Heiko
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb8e7c0 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -253,6 +253,12 @@ config MMC_DW_SNPS This selects support for Synopsys DesignWare Memory Card Interface driver extensions used in various Synopsys ARC devboards.
+config NEXELL_DWMMC
- bool "Nexell SD/MMC controller support"
- depends on ARCH_NEXELL
- depends on MMC_DW
- default y
- config MMC_MESON_GX bool "Meson GX EMMC controller support" depends on DM_MMC && BLK && ARCH_MESON
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..a7b5a7b 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o +obj-$(CONFIG_NEXELL_DWMMC) += nexell_dw_mmc_dm.o
# SDHCI obj-$(CONFIG_MMC_SDHCI) += sdhci.o diff --git a/drivers/mmc/nexell_dw_mmc_dm.c b/drivers/mmc/nexell_dw_mmc_dm.c new file mode 100644 index 0000000..b06b60d --- /dev/null +++ b/drivers/mmc/nexell_dw_mmc_dm.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- Youngbok, Park park@nexell.co.kr
- (C) Copyright 2019 Stefan Bosch stefan_b@posteo.net
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-structs.h> +#include <dwmmc.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/reset.h>
+#define DWMCI_CLKSEL 0x09C +#define DWMCI_SHIFT_0 0x0 +#define DWMCI_SHIFT_1 0x1 +#define DWMCI_SHIFT_2 0x2 +#define DWMCI_SHIFT_3 0x3 +#define DWMCI_SET_SAMPLE_CLK(x) (x) +#define DWMCI_SET_DRV_CLK(x) ((x) << 16) +#define DWMCI_SET_DIV_RATIO(x) ((x) << 24) +#define DWMCI_CLKCTRL 0x114 +#define NX_MMC_CLK_DELAY(x, y, a, b) ((((x) & 0xFF) << 0) |\
(((y) & 0x03) << 16) |\
(((a) & 0xFF) << 8) |\
(((b) & 0x03) << 24))
+struct nexell_mmc_plat {
- struct mmc_config cfg;
- struct mmc mmc;
+};
+struct nexell_dwmmc_priv {
- struct clk *clk;
- struct dwmci_host host;
- int fifo_size;
- bool fifo_mode;
- int frequency;
- u32 min_freq;
- u32 max_freq;
- int d_delay;
- int d_shift;
- int s_delay;
- int s_shift;
+};
+struct clk *clk_get(const char *id);
+static void set_pin_stat(int index, int bit, int value) +{ +#if !defined(CONFIG_SPL_BUILD)
- nx_gpio_set_pad_function(index, bit, value);
+#else +#if defined(CONFIG_ARCH_S5P4418) || \
- defined(CONFIG_ARCH_S5P6818)
- unsigned long base[5] = {
PHY_BASEADDR_GPIOA, PHY_BASEADDR_GPIOB,
PHY_BASEADDR_GPIOC, PHY_BASEADDR_GPIOD,
PHY_BASEADDR_GPIOE,
- };
- dw_mmc_set_pin(base[index], bit, value);
+#endif +#endif +}
+static void nx_dw_mmc_set_pin(struct dwmci_host *host) +{
- debug(" %s(): dev_index == %d", __func__, host->dev_index);
- switch (host->dev_index) {
- case 0:
set_pin_stat(0, 29, 1);
set_pin_stat(0, 31, 1);
set_pin_stat(1, 1, 1);
set_pin_stat(1, 3, 1);
set_pin_stat(1, 5, 1);
set_pin_stat(1, 7, 1);
break;
- case 1:
set_pin_stat(3, 22, 1);
set_pin_stat(3, 23, 1);
set_pin_stat(3, 24, 1);
set_pin_stat(3, 25, 1);
set_pin_stat(3, 26, 1);
set_pin_stat(3, 27, 1);
break;
- case 2:
set_pin_stat(2, 18, 2);
set_pin_stat(2, 19, 2);
set_pin_stat(2, 20, 2);
set_pin_stat(2, 21, 2);
set_pin_stat(2, 22, 2);
set_pin_stat(2, 23, 2);
if (host->buswidth == 8) {
set_pin_stat(4, 21, 2);
set_pin_stat(4, 22, 2);
set_pin_stat(4, 23, 2);
set_pin_stat(4, 24, 2);
}
break;
- default:
debug(" is invalid!");
- }
- debug("\n");
+}
+static void nx_dw_mmc_clksel(struct dwmci_host *host) +{
- u32 val;
+#ifdef CONFIG_BOOST_MMC
- val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(1);
+#else
- val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(3);
+#endif
- dwmci_writel(host, DWMCI_CLKSEL, val);
+}
+static void nx_dw_mmc_reset(int ch) +{
- int rst_id = RESET_ID_SDMMC0 + ch;
- nx_rstcon_setrst(rst_id, 0);
- nx_rstcon_setrst(rst_id, 1);
+}
+static void nx_dw_mmc_clk_delay(struct udevice *dev) +{
- unsigned int delay;
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- delay = NX_MMC_CLK_DELAY(priv->d_delay,
priv->d_shift, priv->s_delay, priv->s_shift);
- writel(delay, (host->ioaddr + DWMCI_CLKCTRL));
- debug("%s(): Values set: d_delay==%d, d_shift==%d, s_delay==%d, "
"s_shift==%d\n", __func__, priv->d_delay, priv->d_shift,
priv->s_delay, priv->s_shift);
+}
+static unsigned int nx_dw_mmc_get_clk(struct dwmci_host *host, uint freq) +{
- struct clk *clk;
- struct udevice *dev = host->priv;
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- int index = host->dev_index;
- char name[50] = { 0, };
- clk = priv->clk;
- if (!clk) {
sprintf(name, "%s.%d", DEV_NAME_SDHC, index);
clk = clk_get((const char *)name);
if (!clk)
return 0;
priv->clk = clk;
- }
- return clk_get_rate(clk) / 2;
+}
+static unsigned long nx_dw_mmc_set_clk(struct dwmci_host *host,
unsigned int rate)
+{
- struct clk *clk;
- char name[50] = { 0, };
- struct udevice *dev = host->priv;
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- int index = host->dev_index;
- clk = priv->clk;
- if (!clk) {
sprintf(name, "%s.%d", DEV_NAME_SDHC, index);
clk = clk_get((const char *)name);
if (!clk)
return 0;
priv->clk = clk;
- }
- clk_disable(clk);
- rate = clk_set_rate(clk, rate);
- clk_enable(clk);
- return rate;
+}
+static int nexell_dwmmc_ofdata_to_platdata(struct udevice *dev) +{
- /* if (dev): *priv = dev->priv, else: *priv = NULL */
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- int val = -1;
- debug("%s()\n", __func__);
- host->name = dev->name;
- host->ioaddr = dev_read_addr_ptr(dev);
- val = dev_read_u32_default(dev, "nexell,bus-width", -1);
- if (val < 0) {
debug(" 'nexell,bus-width' missing/invalid!\n");
return -EINVAL;
- }
- host->buswidth = val;
- host->get_mmc_clk = nx_dw_mmc_get_clk;
- host->clksel = nx_dw_mmc_clksel;
- host->priv = dev;
- val = dev_read_u32_default(dev, "index", -1);
- if (val < 0) {
debug(" 'index' missing/invalid!\n");
return -EINVAL;
- }
- host->dev_index = val;
- val = dev_read_u32_default(dev, "fifo-size", 0x20);
- if (val <= 0) {
debug(" 'fifo-size' missing/invalid!\n");
return -EINVAL;
- }
- priv->fifo_size = val;
- priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
- val = dev_read_u32_default(dev, "frequency", -1);
- if (val < 0) {
debug(" 'frequency' missing/invalid!\n");
return -EINVAL;
- }
- priv->frequency = val;
- val = dev_read_u32_default(dev, "max-frequency", -1);
- if (val < 0) {
debug(" 'max-frequency' missing/invalid!\n");
return -EINVAL;
- }
- priv->max_freq = val;
- priv->min_freq = 400000; /* 400 kHz */
- val = dev_read_u32_default(dev, "nexell,drive_dly", -1);
- if (val < 0) {
debug(" 'nexell,drive_dly' missing/invalid!\n");
return -EINVAL;
- }
- priv->d_delay = val;
- val = dev_read_u32_default(dev, "nexell,drive_shift", -1);
- if (val < 0) {
debug(" 'nexell,drive_shift' missing/invalid!\n");
return -EINVAL;
- }
- priv->d_shift = val;
- val = dev_read_u32_default(dev, "nexell,sample_dly", -1);
- if (val < 0) {
debug(" 'nexell,sample_dly' missing/invalid!\n");
return -EINVAL;
- }
- priv->s_delay = val;
- val = dev_read_u32_default(dev, "nexell,sample_shift", -1);
- if (val < 0) {
debug(" 'nexell,sample_shift' missing/invalid!\n");
return -EINVAL;
- }
- priv->s_shift = val;
- debug(" index==%d, name==%s, ioaddr==0x%08x, buswidth==%d, "
"fifo_size==%d, fifo_mode==%d, frequency==%d\n",
host->dev_index, host->name, (u32)host->ioaddr,
host->buswidth, priv->fifo_size, priv->fifo_mode,
priv->frequency);
- debug(" min_freq==%d, max_freq==%d, delay: "
"0x%02x:0x%02x:0x%02x:0x%02x\n",
priv->min_freq, priv->max_freq, priv->d_delay,
priv->d_shift, priv->s_delay, priv->s_shift);
- return 0;
+}
+static int nexell_dwmmc_probe(struct udevice *dev) +{
- struct nexell_mmc_plat *plat = dev_get_platdata(dev);
- struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- struct udevice *pwr_dev __maybe_unused;
- debug("%s():\n", __func__);
- host->fifoth_val = MSIZE(0x2) |
RX_WMARK(priv->fifo_size / 2 - 1) |
TX_WMARK(priv->fifo_size / 2);
- host->fifo_mode = priv->fifo_mode;
- dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq);
- host->mmc = &plat->mmc;
- host->mmc->priv = &priv->host;
- host->mmc->dev = dev;
- upriv->mmc = host->mmc;
- nx_dw_mmc_set_pin(host);
- debug(" nx_dw_mmc_set_clk(host, frequency * 4 == %d)\n",
priv->frequency * 4);
- nx_dw_mmc_set_clk(host, priv->frequency * 4);
- nx_dw_mmc_reset(host->dev_index);
- nx_dw_mmc_clk_delay(dev);
- return dwmci_probe(dev);
+}
+static int nexell_dwmmc_bind(struct udevice *dev) +{
- struct nexell_mmc_plat *plat = dev_get_platdata(dev);
- return dwmci_bind(dev, &plat->mmc, &plat->cfg);
+}
+static const struct udevice_id nexell_dwmmc_ids[] = {
- { .compatible = "nexell,nexell-dwmmc" },
- { }
+};
+U_BOOT_DRIVER(nexell_dwmmc_drv) = {
- .name = "nexell_dwmmc",
- .id = UCLASS_MMC,
- .of_match = nexell_dwmmc_ids,
- .ofdata_to_platdata = nexell_dwmmc_ofdata_to_platdata,
- .ops = &dm_dwmci_ops,
- .bind = nexell_dwmmc_bind,
- .probe = nexell_dwmmc_probe,
- .priv_auto_alloc_size = sizeof(struct nexell_dwmmc_priv),
- .platdata_auto_alloc_size = sizeof(struct nexell_mmc_plat),
+}; diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a837c35..b45aada 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o +obj-$(CONFIG_PWM_NX) += pwm-nexell.o diff --git a/drivers/pwm/pwm-nexell.c b/drivers/pwm/pwm-nexell.c new file mode 100644 index 0000000..6c0f8f4 --- /dev/null +++ b/drivers/pwm/pwm-nexell.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (C) 2011 Samsung Electronics
- Donghwa Lee dh09.lee@samsung.com
- */
+/* This codes are copied from arch/arm/cpu/armv7/s5p-common/pwm.c */
+#include <common.h> +#include <errno.h> +#include <pwm.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include "pwm-nexell.h"
+#if defined(CONFIG_ARCH_NEXELL) +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/tieoff.h>
+struct pwm_device {
- int ch;
- int grp;
- int bit;
- int pwm_fn;
+};
+static struct pwm_device pwm_dev[] = {
- [0] = { .ch = 0, .grp = 3, .bit = 1, .pwm_fn = 1 },
- [1] = { .ch = 1, .grp = 2, .bit = 13, .pwm_fn = 2 },
- [2] = { .ch = 2, .grp = 2, .bit = 14, .pwm_fn = 2 },
- [3] = { .ch = 3, .grp = 3, .bit = 0, .pwm_fn = 2 },
+}; +#endif
+int pwm_enable(int pwm_id) +{
- const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
(struct s5p_timer *)samsung_get_base_timer();
+#endif
- unsigned long tcon;
- tcon = readl(&pwm->tcon);
- tcon |= TCON_START(pwm_id);
- writel(tcon, &pwm->tcon);
- return 0;
+}
+void pwm_disable(int pwm_id) +{
- const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
(struct s5p_timer *)samsung_get_base_timer();
+#endif
- unsigned long tcon;
- tcon = readl(&pwm->tcon);
- tcon &= ~TCON_START(pwm_id);
- writel(tcon, &pwm->tcon);
+}
+static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq) +{
- unsigned long tin_parent_rate;
- unsigned int div;
+#if defined(CONFIG_ARCH_NEXELL)
- unsigned int pre_div;
- const struct s5p_timer *pwm =
(struct s5p_timer *)PHY_BASEADDR_PWM;
- unsigned int val;
+#endif
+#if defined(CONFIG_ARCH_NEXELL)
- struct clk *clk = clk_get(CORECLK_NAME_PCLK);
- tin_parent_rate = clk_get_rate(clk);
+#else
- tin_parent_rate = get_pwm_clk();
+#endif
+#if defined(CONFIG_ARCH_NEXELL)
- writel(0, &pwm->tcfg0);
- val = readl(&pwm->tcfg0);
- if (pwm_id < 2)
div = ((val >> 0) & 0xff) + 1;
- else
div = ((val >> 8) & 0xff) + 1;
- writel(0, &pwm->tcfg1);
- val = readl(&pwm->tcfg1);
- val = (val >> MUX_DIV_SHIFT(pwm_id)) & 0xF;
- pre_div = (1UL << val);
- freq = tin_parent_rate / div / pre_div;
- return freq;
+#else
- for (div = 2; div <= 16; div *= 2) {
if ((tin_parent_rate / (div << 16)) < freq)
return tin_parent_rate / div;
- }
- return tin_parent_rate / 16;
+#endif +}
+#define NS_IN_SEC 1000000000UL
+int pwm_config(int pwm_id, int duty_ns, int period_ns) +{
- const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
(struct s5p_timer *)samsung_get_base_timer();
+#endif
- unsigned int offset;
- unsigned long tin_rate;
- unsigned long tin_ns;
- unsigned long frequency;
- unsigned long tcon;
- unsigned long tcnt;
- unsigned long tcmp;
- /*
* We currently avoid using 64bit arithmetic by using the
* fact that anything faster than 1GHz is easily representable
* by 32bits.
*/
- if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0)
return -ERANGE;
- if (duty_ns > period_ns)
return -EINVAL;
- frequency = NS_IN_SEC / period_ns;
- /* Check to see if we are changing the clock rate of the PWM */
- tin_rate = pwm_calc_tin(pwm_id, frequency);
- tin_ns = NS_IN_SEC / tin_rate;
+#if defined(CONFIG_ARCH_NEXELL)
- /* The counter starts at zero. */
- tcnt = (period_ns / tin_ns) - 1;
+#else
- tcnt = period_ns / tin_ns;
+#endif
- /* Note, counters count down */
- tcmp = duty_ns / tin_ns;
- tcmp = tcnt - tcmp;
- /* Update the PWM register block. */
- offset = pwm_id * 3;
- if (pwm_id < 4) {
writel(tcnt, &pwm->tcntb0 + offset);
writel(tcmp, &pwm->tcmpb0 + offset);
- }
- tcon = readl(&pwm->tcon);
- tcon |= TCON_UPDATE(pwm_id);
- if (pwm_id < 4)
tcon |= TCON_AUTO_RELOAD(pwm_id);
- else
tcon |= TCON4_AUTO_RELOAD;
- writel(tcon, &pwm->tcon);
- tcon &= ~TCON_UPDATE(pwm_id);
- writel(tcon, &pwm->tcon);
- return 0;
+}
+int pwm_init(int pwm_id, int div, int invert) +{
- u32 val;
- const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
(struct s5p_timer *)samsung_get_base_timer();
+#endif
- unsigned long ticks_per_period;
- unsigned int offset, prescaler;
- /*
* Timer Freq(HZ) =
* PWM_CLK / { (prescaler_value + 1) * (divider_value) }
*/
- val = readl(&pwm->tcfg0);
- if (pwm_id < 2) {
prescaler = PRESCALER_0;
val &= ~0xff;
val |= (prescaler & 0xff);
- } else {
prescaler = PRESCALER_1;
val &= ~(0xff << 8);
val |= (prescaler & 0xff) << 8;
- }
- writel(val, &pwm->tcfg0);
- val = readl(&pwm->tcfg1);
- val &= ~(0xf << MUX_DIV_SHIFT(pwm_id));
- val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id);
- writel(val, &pwm->tcfg1);
- if (pwm_id == 4) {
/*
* TODO(sjg): Use this as a countdown timer for now. We count
* down from the maximum value to 0, then reset.
*/
ticks_per_period = -1UL;
- } else {
const unsigned long pwm_hz = 1000;
+#if defined(CONFIG_ARCH_NEXELL)
struct clk *clk = clk_get(CORECLK_NAME_PCLK);
unsigned long timer_rate_hz = clk_get_rate(clk) /
+#else
unsigned long timer_rate_hz = get_pwm_clk() /
+#endif
((prescaler + 1) * (1 << div));
ticks_per_period = timer_rate_hz / pwm_hz;
- }
- /* set count value */
- offset = pwm_id * 3;
- writel(ticks_per_period, &pwm->tcntb0 + offset);
- val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id));
- if (invert && pwm_id < 4)
val |= TCON_INVERTER(pwm_id);
- writel(val, &pwm->tcon);
- nx_gpio_set_pad_function(pwm_dev[pwm_id].grp, pwm_dev[pwm_id].bit,
pwm_dev[pwm_id].pwm_fn);
- pwm_enable(pwm_id);
- return 0;
+} diff --git a/drivers/pwm/pwm-nexell.h b/drivers/pwm/pwm-nexell.h new file mode 100644 index 0000000..92dc707 --- /dev/null +++ b/drivers/pwm/pwm-nexell.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+
- Copyright (C) 2009 Samsung Electronics
- Kyungmin Park kyungmin.park@samsung.com
- Minkyu Kang mk7.kang@samsung.com
- */
+#ifndef __ASM_ARM_ARCH_PWM_H_ +#define __ASM_ARM_ARCH_PWM_H_
+#define PRESCALER_0 (8 - 1) /* prescaler of timer 0, 1 */ +#define PRESCALER_1 (16 - 1) /* prescaler of timer 2, 3, 4 */
+/* Divider MUX */ +#define MUX_DIV_1 0 /* 1/1 period */ +#define MUX_DIV_2 1 /* 1/2 period */ +#define MUX_DIV_4 2 /* 1/4 period */ +#define MUX_DIV_8 3 /* 1/8 period */ +#define MUX_DIV_16 4 /* 1/16 period */
+#define MUX_DIV_SHIFT(x) ((x) * 4)
+#define TCON_OFFSET(x) (((x) + 1) * (!!x) << 2)
+#define TCON_START(x) (1 << TCON_OFFSET(x)) +#define TCON_UPDATE(x) (1 << (TCON_OFFSET(x) + 1)) +#define TCON_INVERTER(x) (1 << (TCON_OFFSET(x) + 2)) +#define TCON_AUTO_RELOAD(x) (1 << (TCON_OFFSET(x) + 3)) +#define TCON4_AUTO_RELOAD (1 << 22)
+#ifndef __ASSEMBLY__ +struct s5p_timer {
- unsigned int tcfg0;
- unsigned int tcfg1;
- unsigned int tcon;
- unsigned int tcntb0;
- unsigned int tcmpb0;
- unsigned int tcnto0;
- unsigned int tcntb1;
- unsigned int tcmpb1;
- unsigned int tcnto1;
- unsigned int tcntb2;
- unsigned int tcmpb2;
- unsigned int tcnto2;
- unsigned int tcntb3;
- unsigned int res1;
- unsigned int tcnto3;
- unsigned int tcntb4;
- unsigned int tcnto4;
- unsigned int tintcstat;
+}; +#endif /* __ASSEMBLY__ */
+#endif

Hello Heiko,
thanks a lot for your annotations and suggestions. I will have a look at them and give you feedback ASAP.
Regards Stefan
Am 04.02.20 um 07:58 schrieb Heiko Schocher:
Hello Stefan,
Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- i2c/nx_i2c.c: Some adaptions mainly because of changes in
"struct udevice".
- mmc: nexell_dw_mmc.c changed to nexell_dw_mmc_dm.c (switched to DM).
Signed-off-by: Stefan Bosch stefan_b@posteo.net
drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/nx_gpio.c | 252 +++++++++++++++++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/nx_i2c.c | 537 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/nexell_dw_mmc_dm.c | 350 +++++++++++++++++++++++++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-nexell.c | 252 +++++++++++++++++++ drivers/pwm/pwm-nexell.h | 54 +++++
Could you please split this patch into 4 parts (i2c, gpio, mmc and pwm) ?
Thanks!
12 files changed, 1473 insertions(+) create mode 100644 drivers/gpio/nx_gpio.c create mode 100644 drivers/i2c/nx_i2c.c create mode 100644 drivers/mmc/nexell_dw_mmc_dm.c create mode 100644 drivers/pwm/pwm-nexell.c create mode 100644 drivers/pwm/pwm-nexell.h
[...]
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 449046b..e3340de 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o +obj-$(CONFIG_NX_GPIO) += nx_gpio.o
Please keep lists sorted.
diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c new file mode 100644 index 0000000..86472f6 --- /dev/null +++ b/drivers/gpio/nx_gpio.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- DeokJin, Lee truevirtue@nexell.co.kr
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/gpio.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_gpio_regs { + u32 data; /* Data register */ + u32 outputenb; /* Output Enable register */ + u32 detmode[2]; /* Detect Mode Register */ + u32 intenb; /* Interrupt Enable Register */ + u32 det; /* Event Detect Register */ + u32 pad; /* Pad Status Register */ +};
+struct nx_alive_gpio_regs { + u32 pwrgate; /* Power Gating Register */ + u32 reserved0[28]; /* Reserved0 */ + u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */ + u32 outputenb; /* Alive GPIO Output Enable Register */ + u32 outputenb_read; /* Alive GPIO Output Read Register */ + u32 reserved1[3]; /* Reserved1 */ + u32 pad_reset; /* Alive GPIO Output Reset Register */ + u32 data; /* Alive GPIO Output Register */ + u32 pad_read; /* Alive GPIO Pad Read Register */ + u32 reserved2[33]; /* Reserved2 */ + u32 pad; /* Alive GPIO Input Value Register */ +};
+struct nx_gpio_platdata { + void *regs; + int gpio_count; + const char *bank_name; +};
+static int nx_alive_gpio_is_check(struct udevice *dev) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + const char *bank_name = plat->bank_name;
+ if (!strcmp(bank_name, "gpio_alv")) + return 1;
+ return 0; +}
+static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ setbits_le32(®s->outputenb_reset, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + setbits_le32(®s->pad_reset, 1 << pin);
+ setbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_get_value(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + unsigned int mask = 1UL << pin; + unsigned int value;
+ value = (readl(®s->pad_read) & mask) >> pin;
+ return value; +}
+static int nx_alive_gpio_set_value(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->pad_reset, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_get_function(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + unsigned int mask = (1UL << pin); + unsigned int output;
+ output = readl(®s->outputenb_read) & mask;
+ if (output) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +}
+static int nx_gpio_direction_input(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_direction_input(dev, pin);
+ clrbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_gpio_direction_output(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_direction_output(dev, pin, val);
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin);
+ setbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_gpio_get_value(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + unsigned int mask = 1UL << pin; + unsigned int value;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_get_value(dev, pin);
+ value = (readl(®s->pad) & mask) >> pin;
+ return value; +}
+static int nx_gpio_set_value(struct udevice *dev, unsigned int pin, int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_set_value(dev, pin, val);
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin);
+ return 0; +}
+static int nx_gpio_get_function(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + unsigned int mask = (1UL << pin); + unsigned int output;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_get_function(dev, pin);
+ output = readl(®s->outputenb) & mask;
+ if (output) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +}
+static int nx_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+ uc_priv->gpio_count = plat->gpio_count; + uc_priv->bank_name = plat->bank_name;
+ return 0; +}
+static int nx_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+ plat->regs = map_physmem(devfdt_get_addr(dev), + sizeof(struct nx_gpio_regs), + MAP_NOCACHE); + plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->node.of_offset, + "nexell,gpio-bank-width", 32); + plat->bank_name = fdt_getprop(gd->fdt_blob, dev->node.of_offset, + "gpio-bank-name", NULL);
+ return 0; +}
+static const struct dm_gpio_ops nx_gpio_ops = { + .direction_input = nx_gpio_direction_input, + .direction_output = nx_gpio_direction_output, + .get_value = nx_gpio_get_value, + .set_value = nx_gpio_set_value, + .get_function = nx_gpio_get_function, +};
+static const struct udevice_id nx_gpio_ids[] = { + { .compatible = "nexell,nexell-gpio" }, + { } +};
+U_BOOT_DRIVER(nx_gpio) = { + .name = "nx_gpio", + .id = UCLASS_GPIO, + .of_match = nx_gpio_ids, + .ops = &nx_gpio_ops, + .ofdata_to_platdata = nx_gpio_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct nx_gpio_platdata), + .probe = nx_gpio_probe, +}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 03d2fed..2cd0ed3 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -317,6 +317,15 @@ config SYS_MXC_I2C8_SLAVE MXC I2C8 Slave endif +config SYS_I2C_NEXELL + bool "Nexell I2C driver" + depends on DM_I2C + help + Add support for the Nexell I2C driver. This is used with various + Nexell parts such as S5Pxx18 series SoCs. All chips + have several I2C ports and all are provided, controlled by the + device tree.
config SYS_I2C_OMAP24XX bool "TI OMAP2+ I2C driver" depends on ARCH_OMAP2PLUS || ARCH_K3 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f5a471f..64b8ead 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o +obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o diff --git a/drivers/i2c/nx_i2c.c b/drivers/i2c/nx_i2c.c new file mode 100644 index 0000000..a3eec6c --- /dev/null +++ b/drivers/i2c/nx_i2c.c @@ -0,0 +1,537 @@ +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/clk.h> +#include <asm/arch/nx_gpio.h>
+#define I2C_WRITE 0 +#define I2C_READ 1
+#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */
+#define I2CLC_FILTER 0x04 /* SDA filter on*/ +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CSTAT_ABT 0x08 /* Arbitration bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRENB 0x20 /* Interrupt Enable bit */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
+#define I2C_TIMEOUT_MS 10 /* 10 ms */
+#define I2C_M_NOSTOP 0x100
+#ifndef CONFIG_MAX_I2C_NUM +#define CONFIG_MAX_I2C_NUM 3 +#endif
Is this really configurable? If so, I do not find the Kconfig description.
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_i2c_regs { + uint iiccon; + uint iicstat; + uint iicadd; + uint iicds; + uint iiclc; +};
+struct nx_i2c_bus { + uint bus_num; + struct nx_i2c_regs *regs; + uint speed; + uint target_speed; + uint sda_delay; +};
+/* s5pxx18 i2c must be reset before enabled */ +static void i2c_reset(int ch) +{ + int rst_id = RESET_ID_I2C0 + ch;
+ nx_rstcon_setrst(rst_id, 0); + nx_rstcon_setrst(rst_id, 1); +}
+/* FIXME : this func will be removed after reset dm driver ported.
- set mmc pad alternative func.
- */
+static void set_i2c_pad_func(struct nx_i2c_bus *i2c) +{ + switch (i2c->bus_num) { + case 0: + nx_gpio_set_pad_function(3, 2, 1); + nx_gpio_set_pad_function(3, 3, 1); + break; + case 1: + nx_gpio_set_pad_function(3, 4, 1); + nx_gpio_set_pad_function(3, 5, 1); + break; + case 2: + nx_gpio_set_pad_function(3, 6, 1); + nx_gpio_set_pad_function(3, 7, 1); + break; + } +}
Hmm... may this should be moved into a seperate pincontrol driver?
+static uint i2c_get_clkrate(struct nx_i2c_bus *bus) +{ + struct clk *clk; + int index = bus->bus_num; + char name[50] = {0, };
?
+ sprintf(name, "%s.%d", DEV_NAME_I2C, index);
Where is DEV_NAME_I2C defined ?
+ clk = clk_get((const char *)name); + if (!clk) + return -1;
+ return clk_get_rate(clk); +}
+static uint i2c_set_clk(struct nx_i2c_bus *bus, uint enb) +{ + struct clk *clk; + char name[50];
+ sprintf(name, "%s.%d", DEV_NAME_I2C, bus->bus_num); + clk = clk_get((const char *)name); + if (!clk) + return -1;
+ if (enb) { + clk_disable(clk); + clk_enable(clk); + } else { + clk_disable(clk); + }
+ return 0; +}
+/* get i2c module number from base address */ +static uint i2c_get_busnum(struct nx_i2c_bus *bus) +{ + void *base_addr = (void *)PHY_BASEADDR_I2C0; + int i;
+ for (i = 0; i < CONFIG_MAX_I2C_NUM; i++) { + if (base_addr == ((void *)bus->regs)) { + bus->bus_num = i; + return i; + } + base_addr += 0x1000; + }
+ return -1;
return -ENODEV;
Hmm... is there no chance to use seq from struct udevice
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/dm/device.h#L152
?
For example like: https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/mxc_i2c.c#L895
+}
+/* Set SDA line delay */ +static int nx_i2c_set_sda_delay(struct nx_i2c_bus *bus, ulong clkin) +{ + struct nx_i2c_regs *i2c = bus->regs; + uint sda_delay = 0;
+ if (bus->sda_delay) { + sda_delay = clkin * bus->sda_delay; + sda_delay = DIV_ROUND_UP(sda_delay, 1000000); + sda_delay = DIV_ROUND_UP(sda_delay, 5); + if (sda_delay > 3) + sda_delay = 3; + sda_delay |= I2CLC_FILTER; + } else { + sda_delay = 0; + }
+ sda_delay &= 0x7; + writel(sda_delay, &i2c->iiclc);
+ return 0; +}
+/* Calculate the value of the divider and prescaler, set the bus speed. */ +static int nx_i2c_set_bus_speed(struct udevice *dev, uint speed) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + struct nx_i2c_regs *i2c = bus->regs; + unsigned long freq, pres = 16, div;
+ freq = i2c_get_clkrate(bus); + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512;
+ div = 0; + while ((freq / pres / (div + 1)) > speed) + div++;
+ /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + writel((div & 0x0F) | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
+ /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(0x00, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
+ bus->speed = bus->target_speed / (div * pres);
Do you want to allow all values of speeds or may you want to use standard speeds, see:
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L33
+ return 0; +}
+static void nx_i2c_set_clockrate(struct udevice *dev, uint speed) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + ulong clkin;
+ nx_i2c_set_bus_speed(dev, speed); + clkin = bus->speed; /* the actual i2c speed */ + clkin /= 1000; /* clkin now in Khz */ + nx_i2c_set_sda_delay(bus, clkin); +}
+static void i2c_process_node(struct udevice *dev) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob;
+ int node;
+ node = dev->node.of_offset;
+ bus->target_speed = fdtdec_get_int(blob, node, + "nexell,i2c-max-bus-freq", 0); + bus->sda_delay = fdtdec_get_int(blob, node, + "nexell,i2c-sda-delay", 0);
You introdue here new properties, please document them in u-boot:/doc/device-tree-bindings/i2c
Please without "nexell,"
Do you plan to post also a linux i2c driver?
If so, the devicetree bindings should be discussed there to avoid different properties between U-Boot and linux!
+}
+static int nx_i2c_probe(struct udevice *dev) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev);
+ /* get regs */ + bus->regs = (struct nx_i2c_regs *)devfdt_get_addr(dev); + /* calc index */ + if (!i2c_get_busnum(bus)) { + debug("not found i2c number!\n"); + return -1;
please return -ENODEV
+ }
+ /* i2c optional node parsing */ + i2c_process_node(dev); + if (!bus->target_speed) + return -1;
please return here also an errorcode from include/linux/errno.h
Hmm.. if you return here if target_speed is not set, it is not optional!
+ /* reset */ + i2c_reset(bus->bus_num); + /* gpio pad */ + set_i2c_pad_func(bus);
+ /* clock rate */ + i2c_set_clk(bus, 1); + nx_i2c_set_clockrate(dev, bus->target_speed); + i2c_set_clk(bus, 0);
+ return 0; +}
+/* i2c bus busy check */ +static int i2c_is_busy(struct nx_i2c_regs *i2c) +{ + ulong start_time;
+ start_time = get_timer(0); + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) { + debug("Timeout\n"); + return -I2C_NOK_TOUT; + } + } + return 0; +}
+/* irq enable/disable functions */ +static void i2c_enable_irq(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ reg = readl(&i2c->iiccon); + reg |= I2CCON_IRENB; + writel(reg, &i2c->iiccon); +}
+/* irq clear function */ +static void i2c_clear_irq(struct nx_i2c_regs *i2c) +{ + clrbits_le32(&i2c->iiccon, I2CCON_IRPND); +}
+/* ack enable functions */ +static void i2c_enable_ack(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ reg = readl(&i2c->iiccon); + reg |= I2CCON_ACKGEN; + writel(reg, &i2c->iiccon); +}
+static void i2c_send_stop(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ /* Send STOP. */ + reg = readl(&i2c->iicstat); + reg |= I2C_MODE_MR | I2C_TXRX_ENA; + reg &= (~I2C_START_STOP); + writel(reg, &i2c->iicstat); + i2c_clear_irq(i2c); +}
+static int wait_for_xfer(struct nx_i2c_regs *i2c) +{ + unsigned long start_time = get_timer(0);
+ do { + if (readl(&i2c->iiccon) & I2CCON_IRPND) + return (readl(&i2c->iicstat) & I2CSTAT_NACK) ? + I2C_NACK : I2C_OK; + } while (get_timer(start_time) < I2C_TIMEOUT_MS);
+ return I2C_NOK_TOUT; +}
+static int i2c_transfer(struct nx_i2c_regs *i2c, + uchar cmd_type, + uchar chip, + uchar addr[], + uchar addr_len, + uchar data[], + unsigned short data_len, + uint seq) +{ + uint status; + int i = 0, result;
+ if (data == 0 || data_len == 0) { + /*Don't support data transfer of no length or to address 0 */ + debug("%s: bad call\n", __func__); + return I2C_NOK; + }
+ i2c_enable_irq(i2c); + i2c_enable_ack(i2c);
+ /* Get the slave chip address going */ + writel(chip, &i2c->iicds); + status = I2C_TXRX_ENA | I2C_START_STOP; + if (cmd_type == I2C_WRITE || (addr && addr_len)) + status |= I2C_MODE_MT; + else + status |= I2C_MODE_MR; + writel(status, &i2c->iicstat); + if (seq) + i2c_clear_irq(i2c);
+ /* Wait for chip address to transmit. */ + result = wait_for_xfer(i2c); + if (result != I2C_OK) + goto bailout;
+ /* If register address needs to be transmitted - do it now. */ + if (addr && addr_len) { /* register addr */ + while ((i < addr_len) && (result == I2C_OK)) { + writel(addr[i++], &i2c->iicds); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + }
+ i = 0; + if (result != I2C_OK) + goto bailout; + }
+ switch (cmd_type) { + case I2C_WRITE: + while ((i < data_len) && (result == I2C_OK)) { + writel(data[i++], &i2c->iicds); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + } + break; + case I2C_READ: + if (addr && addr_len) { + /* + * Register address has been sent, now send slave chip + * address again to start the actual read transaction. + */ + writel(chip, &i2c->iicds);
+ /* Generate a re-START. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA + | I2C_START_STOP, &i2c->iicstat); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + if (result != I2C_OK) + goto bailout; + }
+ while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + clrbits_le32(&i2c->iiccon + , I2CCON_ACKGEN);
+ i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + data[i++] = readb(&i2c->iicds); + }
+ if (result == I2C_NACK) + result = I2C_OK; /* Normal terminated read. */ + break;
+ default: + debug("%s: bad call\n", __func__); + result = I2C_NOK; + break; + }
+bailout: + return result; +}
+static int nx_i2c_read(struct udevice *dev, uchar chip, uint addr, + uint alen, uchar *buffer, uint len, uint seq) +{ + struct nx_i2c_bus *i2c; + uchar xaddr[4]; + int ret;
+ i2c = dev_get_priv(dev); + if (!i2c) + return -EFAULT;
+ if (alen > 4) { + debug("I2C read: addr len %d not supported\n", alen); + return -EADDRNOTAVAIL; + }
+ if (alen > 0) + xaddr[0] = (addr >> 24) & 0xFF;
+ if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + }
+ ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, + &xaddr[4 - alen], alen, buffer, len, seq);
+ if (ret) { + debug("I2C read failed %d\n", ret); + return -EIO; + }
+ return 0; +}
+static int nx_i2c_write(struct udevice *dev, uchar chip, uint addr, + uint alen, uchar *buffer, uint len, uint seq) +{ + struct nx_i2c_bus *i2c; + uchar xaddr[4]; + int ret;
+ i2c = dev_get_priv(dev); + if (!i2c) + return -EFAULT;
+ if (alen > 4) { + debug("I2C write: addr len %d not supported\n", alen); + return -EINVAL; + }
+ if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + }
+ ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, + &xaddr[4 - alen], alen, buffer, len, seq); + if (ret) { + debug("I2C write failed %d\n", ret); + return -EIO; + }
+ return 0; +}
+static int nx_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + struct nx_i2c_regs *i2c = bus->regs; + int ret; + int i;
+ /* The power loss by the clock, only during on/off. */ + i2c_set_clk(bus, 1);
+ /* Bus State(Busy) check */ + ret = i2c_is_busy(i2c); + if (ret < 0) + return ret;
+ for (i = 0; i < nmsgs; msg++, i++) { + if (msg->flags & I2C_M_RD) { + ret = nx_i2c_read(dev, msg->addr, 0, 0, msg->buf, + msg->len, i); + } else { + ret = nx_i2c_write(dev, msg->addr, 0, 0, msg->buf, + msg->len, i); + }
+ if (ret) { + debug("i2c_xfer: error sending\n"); + return -EREMOTEIO; + } + } + /* Send Stop */ + i2c_send_stop(i2c); + i2c_set_clk(bus, 0);
+ return ret ? -EREMOTEIO : 0; +};
+static const struct dm_i2c_ops nx_i2c_ops = { + .xfer = nx_i2c_xfer, + .set_bus_speed = nx_i2c_set_bus_speed, +};
+static const struct udevice_id nx_i2c_ids[] = { + { .compatible = "nexell,s5pxx18-i2c" },
Same here as for the new properties. Please discuss the names on
devicetree@vger.kernel.org devicetree@vger.kernel.org
first!
+ { } +};
+U_BOOT_DRIVER(i2c_nexell) = { + .name = "i2c_nexell", + .id = UCLASS_I2C, + .of_match = nx_i2c_ids, + .probe = nx_i2c_probe, + .priv_auto_alloc_size = sizeof(struct nx_i2c_bus), + .ops = &nx_i2c_ops, +};
bye, Heiko
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb8e7c0 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -253,6 +253,12 @@ config MMC_DW_SNPS This selects support for Synopsys DesignWare Memory Card Interface driver extensions used in various Synopsys ARC devboards. +config NEXELL_DWMMC + bool "Nexell SD/MMC controller support" + depends on ARCH_NEXELL + depends on MMC_DW + default y
config MMC_MESON_GX bool "Meson GX EMMC controller support" depends on DM_MMC && BLK && ARCH_MESON diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..a7b5a7b 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o +obj-$(CONFIG_NEXELL_DWMMC) += nexell_dw_mmc_dm.o # SDHCI obj-$(CONFIG_MMC_SDHCI) += sdhci.o diff --git a/drivers/mmc/nexell_dw_mmc_dm.c b/drivers/mmc/nexell_dw_mmc_dm.c new file mode 100644 index 0000000..b06b60d --- /dev/null +++ b/drivers/mmc/nexell_dw_mmc_dm.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- Youngbok, Park park@nexell.co.kr
- (C) Copyright 2019 Stefan Bosch stefan_b@posteo.net
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-structs.h> +#include <dwmmc.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/reset.h>
+#define DWMCI_CLKSEL 0x09C +#define DWMCI_SHIFT_0 0x0 +#define DWMCI_SHIFT_1 0x1 +#define DWMCI_SHIFT_2 0x2 +#define DWMCI_SHIFT_3 0x3 +#define DWMCI_SET_SAMPLE_CLK(x) (x) +#define DWMCI_SET_DRV_CLK(x) ((x) << 16) +#define DWMCI_SET_DIV_RATIO(x) ((x) << 24) +#define DWMCI_CLKCTRL 0x114 +#define NX_MMC_CLK_DELAY(x, y, a, b) ((((x) & 0xFF) << 0) |\ + (((y) & 0x03) << 16) |\ + (((a) & 0xFF) << 8) |\ + (((b) & 0x03) << 24))
+struct nexell_mmc_plat { + struct mmc_config cfg; + struct mmc mmc; +};
+struct nexell_dwmmc_priv { + struct clk *clk; + struct dwmci_host host; + int fifo_size; + bool fifo_mode; + int frequency; + u32 min_freq; + u32 max_freq; + int d_delay; + int d_shift; + int s_delay; + int s_shift;
+};
+struct clk *clk_get(const char *id);
+static void set_pin_stat(int index, int bit, int value) +{ +#if !defined(CONFIG_SPL_BUILD) + nx_gpio_set_pad_function(index, bit, value); +#else +#if defined(CONFIG_ARCH_S5P4418) || \ + defined(CONFIG_ARCH_S5P6818)
+ unsigned long base[5] = { + PHY_BASEADDR_GPIOA, PHY_BASEADDR_GPIOB, + PHY_BASEADDR_GPIOC, PHY_BASEADDR_GPIOD, + PHY_BASEADDR_GPIOE, + };
+ dw_mmc_set_pin(base[index], bit, value); +#endif +#endif +}
+static void nx_dw_mmc_set_pin(struct dwmci_host *host) +{ + debug(" %s(): dev_index == %d", __func__, host->dev_index);
+ switch (host->dev_index) { + case 0: + set_pin_stat(0, 29, 1); + set_pin_stat(0, 31, 1); + set_pin_stat(1, 1, 1); + set_pin_stat(1, 3, 1); + set_pin_stat(1, 5, 1); + set_pin_stat(1, 7, 1); + break; + case 1: + set_pin_stat(3, 22, 1); + set_pin_stat(3, 23, 1); + set_pin_stat(3, 24, 1); + set_pin_stat(3, 25, 1); + set_pin_stat(3, 26, 1); + set_pin_stat(3, 27, 1); + break; + case 2: + set_pin_stat(2, 18, 2); + set_pin_stat(2, 19, 2); + set_pin_stat(2, 20, 2); + set_pin_stat(2, 21, 2); + set_pin_stat(2, 22, 2); + set_pin_stat(2, 23, 2); + if (host->buswidth == 8) { + set_pin_stat(4, 21, 2); + set_pin_stat(4, 22, 2); + set_pin_stat(4, 23, 2); + set_pin_stat(4, 24, 2); + } + break; + default: + debug(" is invalid!"); + } + debug("\n"); +}
+static void nx_dw_mmc_clksel(struct dwmci_host *host) +{ + u32 val;
+#ifdef CONFIG_BOOST_MMC + val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | + DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(1); +#else + val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | + DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(3); +#endif
+ dwmci_writel(host, DWMCI_CLKSEL, val); +}
+static void nx_dw_mmc_reset(int ch) +{ + int rst_id = RESET_ID_SDMMC0 + ch;
+ nx_rstcon_setrst(rst_id, 0); + nx_rstcon_setrst(rst_id, 1); +}
+static void nx_dw_mmc_clk_delay(struct udevice *dev) +{ + unsigned int delay; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host;
+ delay = NX_MMC_CLK_DELAY(priv->d_delay, + priv->d_shift, priv->s_delay, priv->s_shift);
+ writel(delay, (host->ioaddr + DWMCI_CLKCTRL)); + debug("%s(): Values set: d_delay==%d, d_shift==%d, s_delay==%d, " + "s_shift==%d\n", __func__, priv->d_delay, priv->d_shift, + priv->s_delay, priv->s_shift); +}
+static unsigned int nx_dw_mmc_get_clk(struct dwmci_host *host, uint freq) +{ + struct clk *clk; + struct udevice *dev = host->priv; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+ int index = host->dev_index; + char name[50] = { 0, };
+ clk = priv->clk; + if (!clk) { + sprintf(name, "%s.%d", DEV_NAME_SDHC, index); + clk = clk_get((const char *)name); + if (!clk) + return 0; + priv->clk = clk; + }
+ return clk_get_rate(clk) / 2; +}
+static unsigned long nx_dw_mmc_set_clk(struct dwmci_host *host, + unsigned int rate) +{ + struct clk *clk; + char name[50] = { 0, }; + struct udevice *dev = host->priv; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+ int index = host->dev_index;
+ clk = priv->clk; + if (!clk) { + sprintf(name, "%s.%d", DEV_NAME_SDHC, index); + clk = clk_get((const char *)name); + if (!clk) + return 0; + priv->clk = clk; + }
+ clk_disable(clk); + rate = clk_set_rate(clk, rate); + clk_enable(clk);
+ return rate; +}
+static int nexell_dwmmc_ofdata_to_platdata(struct udevice *dev) +{ + /* if (dev): *priv = dev->priv, else: *priv = NULL */ + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + int val = -1;
+ debug("%s()\n", __func__);
+ host->name = dev->name; + host->ioaddr = dev_read_addr_ptr(dev);
+ val = dev_read_u32_default(dev, "nexell,bus-width", -1); + if (val < 0) { + debug(" 'nexell,bus-width' missing/invalid!\n"); + return -EINVAL; + } + host->buswidth = val; + host->get_mmc_clk = nx_dw_mmc_get_clk; + host->clksel = nx_dw_mmc_clksel; + host->priv = dev;
+ val = dev_read_u32_default(dev, "index", -1); + if (val < 0) { + debug(" 'index' missing/invalid!\n"); + return -EINVAL; + } + host->dev_index = val;
+ val = dev_read_u32_default(dev, "fifo-size", 0x20); + if (val <= 0) { + debug(" 'fifo-size' missing/invalid!\n"); + return -EINVAL; + } + priv->fifo_size = val;
+ priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
+ val = dev_read_u32_default(dev, "frequency", -1); + if (val < 0) { + debug(" 'frequency' missing/invalid!\n"); + return -EINVAL; + } + priv->frequency = val;
+ val = dev_read_u32_default(dev, "max-frequency", -1); + if (val < 0) { + debug(" 'max-frequency' missing/invalid!\n"); + return -EINVAL; + } + priv->max_freq = val; + priv->min_freq = 400000; /* 400 kHz */
+ val = dev_read_u32_default(dev, "nexell,drive_dly", -1); + if (val < 0) { + debug(" 'nexell,drive_dly' missing/invalid!\n"); + return -EINVAL; + } + priv->d_delay = val;
+ val = dev_read_u32_default(dev, "nexell,drive_shift", -1); + if (val < 0) { + debug(" 'nexell,drive_shift' missing/invalid!\n"); + return -EINVAL; + } + priv->d_shift = val;
+ val = dev_read_u32_default(dev, "nexell,sample_dly", -1); + if (val < 0) { + debug(" 'nexell,sample_dly' missing/invalid!\n"); + return -EINVAL; + } + priv->s_delay = val;
+ val = dev_read_u32_default(dev, "nexell,sample_shift", -1); + if (val < 0) { + debug(" 'nexell,sample_shift' missing/invalid!\n"); + return -EINVAL; + } + priv->s_shift = val;
+ debug(" index==%d, name==%s, ioaddr==0x%08x, buswidth==%d, " + "fifo_size==%d, fifo_mode==%d, frequency==%d\n", + host->dev_index, host->name, (u32)host->ioaddr, + host->buswidth, priv->fifo_size, priv->fifo_mode, + priv->frequency); + debug(" min_freq==%d, max_freq==%d, delay: " + "0x%02x:0x%02x:0x%02x:0x%02x\n", + priv->min_freq, priv->max_freq, priv->d_delay, + priv->d_shift, priv->s_delay, priv->s_shift);
+ return 0; +}
+static int nexell_dwmmc_probe(struct udevice *dev) +{ + struct nexell_mmc_plat *plat = dev_get_platdata(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + struct udevice *pwr_dev __maybe_unused;
+ debug("%s():\n", __func__);
+ host->fifoth_val = MSIZE(0x2) | + RX_WMARK(priv->fifo_size / 2 - 1) | + TX_WMARK(priv->fifo_size / 2);
+ host->fifo_mode = priv->fifo_mode;
+ dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq); + host->mmc = &plat->mmc; + host->mmc->priv = &priv->host; + host->mmc->dev = dev; + upriv->mmc = host->mmc;
+ nx_dw_mmc_set_pin(host);
+ debug(" nx_dw_mmc_set_clk(host, frequency * 4 == %d)\n", + priv->frequency * 4); + nx_dw_mmc_set_clk(host, priv->frequency * 4);
+ nx_dw_mmc_reset(host->dev_index); + nx_dw_mmc_clk_delay(dev);
+ return dwmci_probe(dev); +}
+static int nexell_dwmmc_bind(struct udevice *dev) +{ + struct nexell_mmc_plat *plat = dev_get_platdata(dev);
+ return dwmci_bind(dev, &plat->mmc, &plat->cfg); +}
+static const struct udevice_id nexell_dwmmc_ids[] = { + { .compatible = "nexell,nexell-dwmmc" }, + { } +};
+U_BOOT_DRIVER(nexell_dwmmc_drv) = { + .name = "nexell_dwmmc", + .id = UCLASS_MMC, + .of_match = nexell_dwmmc_ids, + .ofdata_to_platdata = nexell_dwmmc_ofdata_to_platdata, + .ops = &dm_dwmci_ops, + .bind = nexell_dwmmc_bind, + .probe = nexell_dwmmc_probe, + .priv_auto_alloc_size = sizeof(struct nexell_dwmmc_priv), + .platdata_auto_alloc_size = sizeof(struct nexell_mmc_plat), +}; diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a837c35..b45aada 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o +obj-$(CONFIG_PWM_NX) += pwm-nexell.o diff --git a/drivers/pwm/pwm-nexell.c b/drivers/pwm/pwm-nexell.c new file mode 100644 index 0000000..6c0f8f4 --- /dev/null +++ b/drivers/pwm/pwm-nexell.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (C) 2011 Samsung Electronics
- Donghwa Lee dh09.lee@samsung.com
- */
+/* This codes are copied from arch/arm/cpu/armv7/s5p-common/pwm.c */
+#include <common.h> +#include <errno.h> +#include <pwm.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include "pwm-nexell.h"
+#if defined(CONFIG_ARCH_NEXELL) +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/tieoff.h>
+struct pwm_device { + int ch; + int grp; + int bit; + int pwm_fn; +};
+static struct pwm_device pwm_dev[] = { + [0] = { .ch = 0, .grp = 3, .bit = 1, .pwm_fn = 1 }, + [1] = { .ch = 1, .grp = 2, .bit = 13, .pwm_fn = 2 }, + [2] = { .ch = 2, .grp = 2, .bit = 14, .pwm_fn = 2 }, + [3] = { .ch = 3, .grp = 3, .bit = 0, .pwm_fn = 2 }, +}; +#endif
+int pwm_enable(int pwm_id) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long tcon;
+ tcon = readl(&pwm->tcon); + tcon |= TCON_START(pwm_id);
+ writel(tcon, &pwm->tcon);
+ return 0; +}
+void pwm_disable(int pwm_id) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long tcon;
+ tcon = readl(&pwm->tcon); + tcon &= ~TCON_START(pwm_id);
+ writel(tcon, &pwm->tcon); +}
+static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq) +{ + unsigned long tin_parent_rate; + unsigned int div; +#if defined(CONFIG_ARCH_NEXELL) + unsigned int pre_div; + const struct s5p_timer *pwm = + (struct s5p_timer *)PHY_BASEADDR_PWM; + unsigned int val; +#endif
+#if defined(CONFIG_ARCH_NEXELL) + struct clk *clk = clk_get(CORECLK_NAME_PCLK);
+ tin_parent_rate = clk_get_rate(clk); +#else + tin_parent_rate = get_pwm_clk(); +#endif
+#if defined(CONFIG_ARCH_NEXELL) + writel(0, &pwm->tcfg0); + val = readl(&pwm->tcfg0);
+ if (pwm_id < 2) + div = ((val >> 0) & 0xff) + 1; + else + div = ((val >> 8) & 0xff) + 1;
+ writel(0, &pwm->tcfg1); + val = readl(&pwm->tcfg1); + val = (val >> MUX_DIV_SHIFT(pwm_id)) & 0xF; + pre_div = (1UL << val);
+ freq = tin_parent_rate / div / pre_div;
+ return freq; +#else + for (div = 2; div <= 16; div *= 2) { + if ((tin_parent_rate / (div << 16)) < freq) + return tin_parent_rate / div; + }
+ return tin_parent_rate / 16; +#endif +}
+#define NS_IN_SEC 1000000000UL
+int pwm_config(int pwm_id, int duty_ns, int period_ns) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned int offset; + unsigned long tin_rate; + unsigned long tin_ns; + unsigned long frequency; + unsigned long tcon; + unsigned long tcnt; + unsigned long tcmp;
+ /* + * We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1GHz is easily representable + * by 32bits. + */ + if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0) + return -ERANGE;
+ if (duty_ns > period_ns) + return -EINVAL;
+ frequency = NS_IN_SEC / period_ns;
+ /* Check to see if we are changing the clock rate of the PWM */ + tin_rate = pwm_calc_tin(pwm_id, frequency);
+ tin_ns = NS_IN_SEC / tin_rate; +#if defined(CONFIG_ARCH_NEXELL) + /* The counter starts at zero. */ + tcnt = (period_ns / tin_ns) - 1; +#else + tcnt = period_ns / tin_ns; +#endif
+ /* Note, counters count down */ + tcmp = duty_ns / tin_ns; + tcmp = tcnt - tcmp;
+ /* Update the PWM register block. */ + offset = pwm_id * 3; + if (pwm_id < 4) { + writel(tcnt, &pwm->tcntb0 + offset); + writel(tcmp, &pwm->tcmpb0 + offset); + }
+ tcon = readl(&pwm->tcon); + tcon |= TCON_UPDATE(pwm_id); + if (pwm_id < 4) + tcon |= TCON_AUTO_RELOAD(pwm_id); + else + tcon |= TCON4_AUTO_RELOAD; + writel(tcon, &pwm->tcon);
+ tcon &= ~TCON_UPDATE(pwm_id); + writel(tcon, &pwm->tcon);
+ return 0; +}
+int pwm_init(int pwm_id, int div, int invert) +{ + u32 val; + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long ticks_per_period; + unsigned int offset, prescaler;
+ /* + * Timer Freq(HZ) = + * PWM_CLK / { (prescaler_value + 1) * (divider_value) } + */
+ val = readl(&pwm->tcfg0); + if (pwm_id < 2) { + prescaler = PRESCALER_0; + val &= ~0xff; + val |= (prescaler & 0xff); + } else { + prescaler = PRESCALER_1; + val &= ~(0xff << 8); + val |= (prescaler & 0xff) << 8; + } + writel(val, &pwm->tcfg0); + val = readl(&pwm->tcfg1); + val &= ~(0xf << MUX_DIV_SHIFT(pwm_id)); + val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id); + writel(val, &pwm->tcfg1);
+ if (pwm_id == 4) { + /* + * TODO(sjg): Use this as a countdown timer for now. We count + * down from the maximum value to 0, then reset. + */ + ticks_per_period = -1UL; + } else { + const unsigned long pwm_hz = 1000; +#if defined(CONFIG_ARCH_NEXELL) + struct clk *clk = clk_get(CORECLK_NAME_PCLK); + unsigned long timer_rate_hz = clk_get_rate(clk) / +#else + unsigned long timer_rate_hz = get_pwm_clk() / +#endif + ((prescaler + 1) * (1 << div));
+ ticks_per_period = timer_rate_hz / pwm_hz; + }
+ /* set count value */ + offset = pwm_id * 3;
+ writel(ticks_per_period, &pwm->tcntb0 + offset);
+ val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id)); + if (invert && pwm_id < 4) + val |= TCON_INVERTER(pwm_id); + writel(val, &pwm->tcon);
+ nx_gpio_set_pad_function(pwm_dev[pwm_id].grp, pwm_dev[pwm_id].bit, + pwm_dev[pwm_id].pwm_fn); + pwm_enable(pwm_id);
+ return 0; +} diff --git a/drivers/pwm/pwm-nexell.h b/drivers/pwm/pwm-nexell.h new file mode 100644 index 0000000..92dc707 --- /dev/null +++ b/drivers/pwm/pwm-nexell.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+
- Copyright (C) 2009 Samsung Electronics
- Kyungmin Park kyungmin.park@samsung.com
- Minkyu Kang mk7.kang@samsung.com
- */
+#ifndef __ASM_ARM_ARCH_PWM_H_ +#define __ASM_ARM_ARCH_PWM_H_
+#define PRESCALER_0 (8 - 1) /* prescaler of timer 0, 1 */ +#define PRESCALER_1 (16 - 1) /* prescaler of timer 2, 3, 4 */
+/* Divider MUX */ +#define MUX_DIV_1 0 /* 1/1 period */ +#define MUX_DIV_2 1 /* 1/2 period */ +#define MUX_DIV_4 2 /* 1/4 period */ +#define MUX_DIV_8 3 /* 1/8 period */ +#define MUX_DIV_16 4 /* 1/16 period */
+#define MUX_DIV_SHIFT(x) ((x) * 4)
+#define TCON_OFFSET(x) (((x) + 1) * (!!x) << 2)
+#define TCON_START(x) (1 << TCON_OFFSET(x)) +#define TCON_UPDATE(x) (1 << (TCON_OFFSET(x) + 1)) +#define TCON_INVERTER(x) (1 << (TCON_OFFSET(x) + 2)) +#define TCON_AUTO_RELOAD(x) (1 << (TCON_OFFSET(x) + 3)) +#define TCON4_AUTO_RELOAD (1 << 22)
+#ifndef __ASSEMBLY__ +struct s5p_timer { + unsigned int tcfg0; + unsigned int tcfg1; + unsigned int tcon; + unsigned int tcntb0; + unsigned int tcmpb0; + unsigned int tcnto0; + unsigned int tcntb1; + unsigned int tcmpb1; + unsigned int tcnto1; + unsigned int tcntb2; + unsigned int tcmpb2; + unsigned int tcnto2; + unsigned int tcntb3; + unsigned int res1; + unsigned int tcnto3; + unsigned int tcntb4; + unsigned int tcnto4; + unsigned int tintcstat; +}; +#endif /* __ASSEMBLY__ */
+#endif

Hello Heiko,
see below my feedback, please give me further advise where indicated.
Unfortunately there have been some Bugs in the i2c-driver and I learned that this driver has not been used at all ("i2c-gpio" has been used instead). So I have done several Bugfixes and improvements appart from your proposals.
Regards Stefan
Am 04.02.20 um 07:58 schrieb Heiko Schocher:
Hello Stefan,
Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- i2c/nx_i2c.c: Some adaptions mainly because of changes in
"struct udevice".
- mmc: nexell_dw_mmc.c changed to nexell_dw_mmc_dm.c (switched to DM).
Signed-off-by: Stefan Bosch stefan_b@posteo.net
drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/nx_gpio.c | 252 +++++++++++++++++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/nx_i2c.c | 537 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/nexell_dw_mmc_dm.c | 350 +++++++++++++++++++++++++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-nexell.c | 252 +++++++++++++++++++ drivers/pwm/pwm-nexell.h | 54 +++++
Could you please split this patch into 4 parts (i2c, gpio, mmc and pwm) ?
Thanks!
Ok, I will split this patch.
12 files changed, 1473 insertions(+) create mode 100644 drivers/gpio/nx_gpio.c create mode 100644 drivers/i2c/nx_i2c.c create mode 100644 drivers/mmc/nexell_dw_mmc_dm.c create mode 100644 drivers/pwm/pwm-nexell.c create mode 100644 drivers/pwm/pwm-nexell.h
[...]
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 449046b..e3340de 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o +obj-$(CONFIG_NX_GPIO) += nx_gpio.o
Please keep lists sorted.
The list is not sorted (at least in no alphabetical order), but I can e.g. move "... += nx_gpio.o" one line up?
diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c new file mode 100644 index 0000000..86472f6 --- /dev/null +++ b/drivers/gpio/nx_gpio.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- DeokJin, Lee truevirtue@nexell.co.kr
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/gpio.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_gpio_regs { + u32 data; /* Data register */ + u32 outputenb; /* Output Enable register */ + u32 detmode[2]; /* Detect Mode Register */ + u32 intenb; /* Interrupt Enable Register */ + u32 det; /* Event Detect Register */ + u32 pad; /* Pad Status Register */ +};
+struct nx_alive_gpio_regs { + u32 pwrgate; /* Power Gating Register */ + u32 reserved0[28]; /* Reserved0 */ + u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */ + u32 outputenb; /* Alive GPIO Output Enable Register */ + u32 outputenb_read; /* Alive GPIO Output Read Register */ + u32 reserved1[3]; /* Reserved1 */ + u32 pad_reset; /* Alive GPIO Output Reset Register */ + u32 data; /* Alive GPIO Output Register */ + u32 pad_read; /* Alive GPIO Pad Read Register */ + u32 reserved2[33]; /* Reserved2 */ + u32 pad; /* Alive GPIO Input Value Register */ +};
+struct nx_gpio_platdata { + void *regs; + int gpio_count; + const char *bank_name; +};
+static int nx_alive_gpio_is_check(struct udevice *dev) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + const char *bank_name = plat->bank_name;
+ if (!strcmp(bank_name, "gpio_alv")) + return 1;
+ return 0; +}
+static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ setbits_le32(®s->outputenb_reset, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + setbits_le32(®s->pad_reset, 1 << pin);
+ setbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_get_value(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + unsigned int mask = 1UL << pin; + unsigned int value;
+ value = (readl(®s->pad_read) & mask) >> pin;
+ return value; +}
+static int nx_alive_gpio_set_value(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->pad_reset, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_get_function(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + unsigned int mask = (1UL << pin); + unsigned int output;
+ output = readl(®s->outputenb_read) & mask;
+ if (output) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +}
+static int nx_gpio_direction_input(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_direction_input(dev, pin);
+ clrbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_gpio_direction_output(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_direction_output(dev, pin, val);
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin);
+ setbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_gpio_get_value(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + unsigned int mask = 1UL << pin; + unsigned int value;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_get_value(dev, pin);
+ value = (readl(®s->pad) & mask) >> pin;
+ return value; +}
+static int nx_gpio_set_value(struct udevice *dev, unsigned int pin, int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_set_value(dev, pin, val);
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin);
+ return 0; +}
+static int nx_gpio_get_function(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + unsigned int mask = (1UL << pin); + unsigned int output;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_get_function(dev, pin);
+ output = readl(®s->outputenb) & mask;
+ if (output) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +}
+static int nx_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+ uc_priv->gpio_count = plat->gpio_count; + uc_priv->bank_name = plat->bank_name;
+ return 0; +}
+static int nx_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+ plat->regs = map_physmem(devfdt_get_addr(dev), + sizeof(struct nx_gpio_regs), + MAP_NOCACHE); + plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->node.of_offset, + "nexell,gpio-bank-width", 32); + plat->bank_name = fdt_getprop(gd->fdt_blob, dev->node.of_offset, + "gpio-bank-name", NULL);
+ return 0; +}
+static const struct dm_gpio_ops nx_gpio_ops = { + .direction_input = nx_gpio_direction_input, + .direction_output = nx_gpio_direction_output, + .get_value = nx_gpio_get_value, + .set_value = nx_gpio_set_value, + .get_function = nx_gpio_get_function, +};
+static const struct udevice_id nx_gpio_ids[] = { + { .compatible = "nexell,nexell-gpio" }, + { } +};
+U_BOOT_DRIVER(nx_gpio) = { + .name = "nx_gpio", + .id = UCLASS_GPIO, + .of_match = nx_gpio_ids, + .ops = &nx_gpio_ops, + .ofdata_to_platdata = nx_gpio_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct nx_gpio_platdata), + .probe = nx_gpio_probe, +}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 03d2fed..2cd0ed3 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -317,6 +317,15 @@ config SYS_MXC_I2C8_SLAVE MXC I2C8 Slave endif +config SYS_I2C_NEXELL + bool "Nexell I2C driver" + depends on DM_I2C + help + Add support for the Nexell I2C driver. This is used with various + Nexell parts such as S5Pxx18 series SoCs. All chips + have several I2C ports and all are provided, controlled by the + device tree.
config SYS_I2C_OMAP24XX bool "TI OMAP2+ I2C driver" depends on ARCH_OMAP2PLUS || ARCH_K3 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f5a471f..64b8ead 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o +obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o diff --git a/drivers/i2c/nx_i2c.c b/drivers/i2c/nx_i2c.c new file mode 100644 index 0000000..a3eec6c --- /dev/null +++ b/drivers/i2c/nx_i2c.c @@ -0,0 +1,537 @@ +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/clk.h> +#include <asm/arch/nx_gpio.h>
+#define I2C_WRITE 0 +#define I2C_READ 1
+#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */
+#define I2CLC_FILTER 0x04 /* SDA filter on*/ +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CSTAT_ABT 0x08 /* Arbitration bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRENB 0x20 /* Interrupt Enable bit */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
+#define I2C_TIMEOUT_MS 10 /* 10 ms */
+#define I2C_M_NOSTOP 0x100
+#ifndef CONFIG_MAX_I2C_NUM +#define CONFIG_MAX_I2C_NUM 3 +#endif
Is this really configurable? If so, I do not find the Kconfig description.
No, it is not configurable. I have changed it to MAX_I2C_NUM.
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_i2c_regs { + uint iiccon; + uint iicstat; + uint iicadd; + uint iicds; + uint iiclc; +};
+struct nx_i2c_bus { + uint bus_num; + struct nx_i2c_regs *regs; + uint speed; + uint target_speed; + uint sda_delay; +};
+/* s5pxx18 i2c must be reset before enabled */ +static void i2c_reset(int ch) +{ + int rst_id = RESET_ID_I2C0 + ch;
+ nx_rstcon_setrst(rst_id, 0); + nx_rstcon_setrst(rst_id, 1); +}
+/* FIXME : this func will be removed after reset dm driver ported.
- set mmc pad alternative func.
- */
+static void set_i2c_pad_func(struct nx_i2c_bus *i2c) +{ + switch (i2c->bus_num) { + case 0: + nx_gpio_set_pad_function(3, 2, 1); + nx_gpio_set_pad_function(3, 3, 1); + break; + case 1: + nx_gpio_set_pad_function(3, 4, 1); + nx_gpio_set_pad_function(3, 5, 1); + break; + case 2: + nx_gpio_set_pad_function(3, 6, 1); + nx_gpio_set_pad_function(3, 7, 1); + break; + } +}
Hmm... may this should be moved into a seperate pincontrol driver?
According to the above FIXME comment from Nexell it probably should. But there is no pincontrol driver implemented. But is the change to a driver necessary for now?
+static uint i2c_get_clkrate(struct nx_i2c_bus *bus) +{ + struct clk *clk; + int index = bus->bus_num; + char name[50] = {0, };
?
+ sprintf(name, "%s.%d", DEV_NAME_I2C, index);
Where is DEV_NAME_I2C defined ?
DEV_NAME_I2C is defined in arch/arm/mach-nexell/include/mach/nexell.h
+ clk = clk_get((const char *)name); + if (!clk) + return -1;
+ return clk_get_rate(clk); +}
+static uint i2c_set_clk(struct nx_i2c_bus *bus, uint enb) +{ + struct clk *clk; + char name[50];
+ sprintf(name, "%s.%d", DEV_NAME_I2C, bus->bus_num); + clk = clk_get((const char *)name); + if (!clk) + return -1;
+ if (enb) { + clk_disable(clk); + clk_enable(clk); + } else { + clk_disable(clk); + }
+ return 0; +}
+/* get i2c module number from base address */ +static uint i2c_get_busnum(struct nx_i2c_bus *bus) +{ + void *base_addr = (void *)PHY_BASEADDR_I2C0; + int i;
+ for (i = 0; i < CONFIG_MAX_I2C_NUM; i++) { + if (base_addr == ((void *)bus->regs)) { + bus->bus_num = i; + return i; + } + base_addr += 0x1000; + }
+ return -1;
return -ENODEV;
Hmm... is there no chance to use seq from struct udevice
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/dm/device.h#L152
?
For example like: https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/mxc_i2c.c#L895
Ok, I have changed this as proposed.
+}
+/* Set SDA line delay */ +static int nx_i2c_set_sda_delay(struct nx_i2c_bus *bus, ulong clkin) +{ + struct nx_i2c_regs *i2c = bus->regs; + uint sda_delay = 0;
+ if (bus->sda_delay) { + sda_delay = clkin * bus->sda_delay; + sda_delay = DIV_ROUND_UP(sda_delay, 1000000); + sda_delay = DIV_ROUND_UP(sda_delay, 5); + if (sda_delay > 3) + sda_delay = 3; + sda_delay |= I2CLC_FILTER; + } else { + sda_delay = 0; + }
+ sda_delay &= 0x7; + writel(sda_delay, &i2c->iiclc);
+ return 0; +}
+/* Calculate the value of the divider and prescaler, set the bus speed. */ +static int nx_i2c_set_bus_speed(struct udevice *dev, uint speed) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + struct nx_i2c_regs *i2c = bus->regs; + unsigned long freq, pres = 16, div;
+ freq = i2c_get_clkrate(bus); + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512;
+ div = 0; + while ((freq / pres / (div + 1)) > speed) + div++;
+ /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + writel((div & 0x0F) | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
+ /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(0x00, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
+ bus->speed = bus->target_speed / (div * pres);
Do you want to allow all values of speeds or may you want to use standard speeds, see:
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L33
I'd like to allow all values of speed. In my opinion allowing only standard speeds does complicate things (e.g. how to do error handling?). Furthermore I think sometimes it could be handy to be able to set speed to an arbitrary value (e.g. to a lower value than 100000) when trying a new i2c-device on the bus.
+ return 0; +}
+static void nx_i2c_set_clockrate(struct udevice *dev, uint speed) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + ulong clkin;
+ nx_i2c_set_bus_speed(dev, speed); + clkin = bus->speed; /* the actual i2c speed */ + clkin /= 1000; /* clkin now in Khz */ + nx_i2c_set_sda_delay(bus, clkin); +}
+static void i2c_process_node(struct udevice *dev) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob;
+ int node;
+ node = dev->node.of_offset;
+ bus->target_speed = fdtdec_get_int(blob, node, + "nexell,i2c-max-bus-freq", 0); + bus->sda_delay = fdtdec_get_int(blob, node, + "nexell,i2c-sda-delay", 0);
You introdue here new properties, please document them in u-boot:/doc/device-tree-bindings/i2c
Please without "nexell,"
I have changed "nexell,i2c-max-bus-freq" to the already defined "clock-frequency". Furthermore I have changed "nexell,i2c-sda-delay" to "i2c-sda-delay-ns". Furthermore, I have added "nx_i2c.txt" in doc/device-tree-bindings/i2c.
Do you plan to post also a linux i2c driver?
If so, the devicetree bindings should be discussed there to avoid different properties between U-Boot and linux!
No, I do not plan to post a linux driver.
+}
+static int nx_i2c_probe(struct udevice *dev) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev);
+ /* get regs */ + bus->regs = (struct nx_i2c_regs *)devfdt_get_addr(dev); + /* calc index */ + if (!i2c_get_busnum(bus)) { + debug("not found i2c number!\n"); + return -1;
please return -ENODEV
Ok
+ }
+ /* i2c optional node parsing */ + i2c_process_node(dev); + if (!bus->target_speed) + return -1;
please return here also an errorcode from include/linux/errno.h
Ok
Hmm.. if you return here if target_speed is not set, it is not optional!
You are right, I have removed 'optional' in the comment.
+ /* reset */ + i2c_reset(bus->bus_num); + /* gpio pad */ + set_i2c_pad_func(bus);
+ /* clock rate */ + i2c_set_clk(bus, 1); + nx_i2c_set_clockrate(dev, bus->target_speed); + i2c_set_clk(bus, 0);
+ return 0; +}
+/* i2c bus busy check */ +static int i2c_is_busy(struct nx_i2c_regs *i2c) +{ + ulong start_time;
+ start_time = get_timer(0); + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) { + debug("Timeout\n"); + return -I2C_NOK_TOUT; + } + } + return 0; +}
+/* irq enable/disable functions */ +static void i2c_enable_irq(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ reg = readl(&i2c->iiccon); + reg |= I2CCON_IRENB; + writel(reg, &i2c->iiccon); +}
+/* irq clear function */ +static void i2c_clear_irq(struct nx_i2c_regs *i2c) +{ + clrbits_le32(&i2c->iiccon, I2CCON_IRPND); +}
+/* ack enable functions */ +static void i2c_enable_ack(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ reg = readl(&i2c->iiccon); + reg |= I2CCON_ACKGEN; + writel(reg, &i2c->iiccon); +}
+static void i2c_send_stop(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ /* Send STOP. */ + reg = readl(&i2c->iicstat); + reg |= I2C_MODE_MR | I2C_TXRX_ENA; + reg &= (~I2C_START_STOP); + writel(reg, &i2c->iicstat); + i2c_clear_irq(i2c); +}
+static int wait_for_xfer(struct nx_i2c_regs *i2c) +{ + unsigned long start_time = get_timer(0);
+ do { + if (readl(&i2c->iiccon) & I2CCON_IRPND) + return (readl(&i2c->iicstat) & I2CSTAT_NACK) ? + I2C_NACK : I2C_OK; + } while (get_timer(start_time) < I2C_TIMEOUT_MS);
+ return I2C_NOK_TOUT; +}
+static int i2c_transfer(struct nx_i2c_regs *i2c, + uchar cmd_type, + uchar chip, + uchar addr[], + uchar addr_len, + uchar data[], + unsigned short data_len, + uint seq) +{ + uint status; + int i = 0, result;
+ if (data == 0 || data_len == 0) { + /*Don't support data transfer of no length or to address 0 */ + debug("%s: bad call\n", __func__); + return I2C_NOK; + }
+ i2c_enable_irq(i2c); + i2c_enable_ack(i2c);
+ /* Get the slave chip address going */ + writel(chip, &i2c->iicds); + status = I2C_TXRX_ENA | I2C_START_STOP; + if (cmd_type == I2C_WRITE || (addr && addr_len)) + status |= I2C_MODE_MT; + else + status |= I2C_MODE_MR; + writel(status, &i2c->iicstat); + if (seq) + i2c_clear_irq(i2c);
+ /* Wait for chip address to transmit. */ + result = wait_for_xfer(i2c); + if (result != I2C_OK) + goto bailout;
+ /* If register address needs to be transmitted - do it now. */ + if (addr && addr_len) { /* register addr */ + while ((i < addr_len) && (result == I2C_OK)) { + writel(addr[i++], &i2c->iicds); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + }
+ i = 0; + if (result != I2C_OK) + goto bailout; + }
+ switch (cmd_type) { + case I2C_WRITE: + while ((i < data_len) && (result == I2C_OK)) { + writel(data[i++], &i2c->iicds); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + } + break; + case I2C_READ: + if (addr && addr_len) { + /* + * Register address has been sent, now send slave chip + * address again to start the actual read transaction. + */ + writel(chip, &i2c->iicds);
+ /* Generate a re-START. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA + | I2C_START_STOP, &i2c->iicstat); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + if (result != I2C_OK) + goto bailout; + }
+ while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + clrbits_le32(&i2c->iiccon + , I2CCON_ACKGEN);
+ i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + data[i++] = readb(&i2c->iicds); + }
+ if (result == I2C_NACK) + result = I2C_OK; /* Normal terminated read. */ + break;
+ default: + debug("%s: bad call\n", __func__); + result = I2C_NOK; + break; + }
+bailout: + return result; +}
+static int nx_i2c_read(struct udevice *dev, uchar chip, uint addr, + uint alen, uchar *buffer, uint len, uint seq) +{ + struct nx_i2c_bus *i2c; + uchar xaddr[4]; + int ret;
+ i2c = dev_get_priv(dev); + if (!i2c) + return -EFAULT;
+ if (alen > 4) { + debug("I2C read: addr len %d not supported\n", alen); + return -EADDRNOTAVAIL; + }
+ if (alen > 0) + xaddr[0] = (addr >> 24) & 0xFF;
+ if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + }
+ ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, + &xaddr[4 - alen], alen, buffer, len, seq);
+ if (ret) { + debug("I2C read failed %d\n", ret); + return -EIO; + }
+ return 0; +}
+static int nx_i2c_write(struct udevice *dev, uchar chip, uint addr, + uint alen, uchar *buffer, uint len, uint seq) +{ + struct nx_i2c_bus *i2c; + uchar xaddr[4]; + int ret;
+ i2c = dev_get_priv(dev); + if (!i2c) + return -EFAULT;
+ if (alen > 4) { + debug("I2C write: addr len %d not supported\n", alen); + return -EINVAL; + }
+ if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + }
+ ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, + &xaddr[4 - alen], alen, buffer, len, seq); + if (ret) { + debug("I2C write failed %d\n", ret); + return -EIO; + }
+ return 0; +}
+static int nx_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + struct nx_i2c_regs *i2c = bus->regs; + int ret; + int i;
+ /* The power loss by the clock, only during on/off. */ + i2c_set_clk(bus, 1);
+ /* Bus State(Busy) check */ + ret = i2c_is_busy(i2c); + if (ret < 0) + return ret;
+ for (i = 0; i < nmsgs; msg++, i++) { + if (msg->flags & I2C_M_RD) { + ret = nx_i2c_read(dev, msg->addr, 0, 0, msg->buf, + msg->len, i); + } else { + ret = nx_i2c_write(dev, msg->addr, 0, 0, msg->buf, + msg->len, i); + }
+ if (ret) { + debug("i2c_xfer: error sending\n"); + return -EREMOTEIO; + } + } + /* Send Stop */ + i2c_send_stop(i2c); + i2c_set_clk(bus, 0);
+ return ret ? -EREMOTEIO : 0; +};
+static const struct dm_i2c_ops nx_i2c_ops = { + .xfer = nx_i2c_xfer, + .set_bus_speed = nx_i2c_set_bus_speed, +};
+static const struct udevice_id nx_i2c_ids[] = { + { .compatible = "nexell,s5pxx18-i2c" },
Same here as for the new properties. Please discuss the names on
devicetree@vger.kernel.org devicetree@vger.kernel.org
first!
+ { } +};
+U_BOOT_DRIVER(i2c_nexell) = { + .name = "i2c_nexell", + .id = UCLASS_I2C, + .of_match = nx_i2c_ids, + .probe = nx_i2c_probe, + .priv_auto_alloc_size = sizeof(struct nx_i2c_bus), + .ops = &nx_i2c_ops, +};
bye, Heiko
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb8e7c0 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -253,6 +253,12 @@ config MMC_DW_SNPS This selects support for Synopsys DesignWare Memory Card Interface driver extensions used in various Synopsys ARC devboards. +config NEXELL_DWMMC + bool "Nexell SD/MMC controller support" + depends on ARCH_NEXELL + depends on MMC_DW + default y
config MMC_MESON_GX bool "Meson GX EMMC controller support" depends on DM_MMC && BLK && ARCH_MESON diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..a7b5a7b 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o +obj-$(CONFIG_NEXELL_DWMMC) += nexell_dw_mmc_dm.o # SDHCI obj-$(CONFIG_MMC_SDHCI) += sdhci.o diff --git a/drivers/mmc/nexell_dw_mmc_dm.c b/drivers/mmc/nexell_dw_mmc_dm.c new file mode 100644 index 0000000..b06b60d --- /dev/null +++ b/drivers/mmc/nexell_dw_mmc_dm.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- Youngbok, Park park@nexell.co.kr
- (C) Copyright 2019 Stefan Bosch stefan_b@posteo.net
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-structs.h> +#include <dwmmc.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/reset.h>
+#define DWMCI_CLKSEL 0x09C +#define DWMCI_SHIFT_0 0x0 +#define DWMCI_SHIFT_1 0x1 +#define DWMCI_SHIFT_2 0x2 +#define DWMCI_SHIFT_3 0x3 +#define DWMCI_SET_SAMPLE_CLK(x) (x) +#define DWMCI_SET_DRV_CLK(x) ((x) << 16) +#define DWMCI_SET_DIV_RATIO(x) ((x) << 24) +#define DWMCI_CLKCTRL 0x114 +#define NX_MMC_CLK_DELAY(x, y, a, b) ((((x) & 0xFF) << 0) |\ + (((y) & 0x03) << 16) |\ + (((a) & 0xFF) << 8) |\ + (((b) & 0x03) << 24))
+struct nexell_mmc_plat { + struct mmc_config cfg; + struct mmc mmc; +};
+struct nexell_dwmmc_priv { + struct clk *clk; + struct dwmci_host host; + int fifo_size; + bool fifo_mode; + int frequency; + u32 min_freq; + u32 max_freq; + int d_delay; + int d_shift; + int s_delay; + int s_shift;
+};
+struct clk *clk_get(const char *id);
+static void set_pin_stat(int index, int bit, int value) +{ +#if !defined(CONFIG_SPL_BUILD) + nx_gpio_set_pad_function(index, bit, value); +#else +#if defined(CONFIG_ARCH_S5P4418) || \ + defined(CONFIG_ARCH_S5P6818)
+ unsigned long base[5] = { + PHY_BASEADDR_GPIOA, PHY_BASEADDR_GPIOB, + PHY_BASEADDR_GPIOC, PHY_BASEADDR_GPIOD, + PHY_BASEADDR_GPIOE, + };
+ dw_mmc_set_pin(base[index], bit, value); +#endif +#endif +}
+static void nx_dw_mmc_set_pin(struct dwmci_host *host) +{ + debug(" %s(): dev_index == %d", __func__, host->dev_index);
+ switch (host->dev_index) { + case 0: + set_pin_stat(0, 29, 1); + set_pin_stat(0, 31, 1); + set_pin_stat(1, 1, 1); + set_pin_stat(1, 3, 1); + set_pin_stat(1, 5, 1); + set_pin_stat(1, 7, 1); + break; + case 1: + set_pin_stat(3, 22, 1); + set_pin_stat(3, 23, 1); + set_pin_stat(3, 24, 1); + set_pin_stat(3, 25, 1); + set_pin_stat(3, 26, 1); + set_pin_stat(3, 27, 1); + break; + case 2: + set_pin_stat(2, 18, 2); + set_pin_stat(2, 19, 2); + set_pin_stat(2, 20, 2); + set_pin_stat(2, 21, 2); + set_pin_stat(2, 22, 2); + set_pin_stat(2, 23, 2); + if (host->buswidth == 8) { + set_pin_stat(4, 21, 2); + set_pin_stat(4, 22, 2); + set_pin_stat(4, 23, 2); + set_pin_stat(4, 24, 2); + } + break; + default: + debug(" is invalid!"); + } + debug("\n"); +}
+static void nx_dw_mmc_clksel(struct dwmci_host *host) +{ + u32 val;
+#ifdef CONFIG_BOOST_MMC + val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | + DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(1); +#else + val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | + DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(3); +#endif
+ dwmci_writel(host, DWMCI_CLKSEL, val); +}
+static void nx_dw_mmc_reset(int ch) +{ + int rst_id = RESET_ID_SDMMC0 + ch;
+ nx_rstcon_setrst(rst_id, 0); + nx_rstcon_setrst(rst_id, 1); +}
+static void nx_dw_mmc_clk_delay(struct udevice *dev) +{ + unsigned int delay; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host;
+ delay = NX_MMC_CLK_DELAY(priv->d_delay, + priv->d_shift, priv->s_delay, priv->s_shift);
+ writel(delay, (host->ioaddr + DWMCI_CLKCTRL)); + debug("%s(): Values set: d_delay==%d, d_shift==%d, s_delay==%d, " + "s_shift==%d\n", __func__, priv->d_delay, priv->d_shift, + priv->s_delay, priv->s_shift); +}
+static unsigned int nx_dw_mmc_get_clk(struct dwmci_host *host, uint freq) +{ + struct clk *clk; + struct udevice *dev = host->priv; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+ int index = host->dev_index; + char name[50] = { 0, };
+ clk = priv->clk; + if (!clk) { + sprintf(name, "%s.%d", DEV_NAME_SDHC, index); + clk = clk_get((const char *)name); + if (!clk) + return 0; + priv->clk = clk; + }
+ return clk_get_rate(clk) / 2; +}
+static unsigned long nx_dw_mmc_set_clk(struct dwmci_host *host, + unsigned int rate) +{ + struct clk *clk; + char name[50] = { 0, }; + struct udevice *dev = host->priv; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+ int index = host->dev_index;
+ clk = priv->clk; + if (!clk) { + sprintf(name, "%s.%d", DEV_NAME_SDHC, index); + clk = clk_get((const char *)name); + if (!clk) + return 0; + priv->clk = clk; + }
+ clk_disable(clk); + rate = clk_set_rate(clk, rate); + clk_enable(clk);
+ return rate; +}
+static int nexell_dwmmc_ofdata_to_platdata(struct udevice *dev) +{ + /* if (dev): *priv = dev->priv, else: *priv = NULL */ + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + int val = -1;
+ debug("%s()\n", __func__);
+ host->name = dev->name; + host->ioaddr = dev_read_addr_ptr(dev);
+ val = dev_read_u32_default(dev, "nexell,bus-width", -1); + if (val < 0) { + debug(" 'nexell,bus-width' missing/invalid!\n"); + return -EINVAL; + } + host->buswidth = val; + host->get_mmc_clk = nx_dw_mmc_get_clk; + host->clksel = nx_dw_mmc_clksel; + host->priv = dev;
+ val = dev_read_u32_default(dev, "index", -1); + if (val < 0) { + debug(" 'index' missing/invalid!\n"); + return -EINVAL; + } + host->dev_index = val;
+ val = dev_read_u32_default(dev, "fifo-size", 0x20); + if (val <= 0) { + debug(" 'fifo-size' missing/invalid!\n"); + return -EINVAL; + } + priv->fifo_size = val;
+ priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
+ val = dev_read_u32_default(dev, "frequency", -1); + if (val < 0) { + debug(" 'frequency' missing/invalid!\n"); + return -EINVAL; + } + priv->frequency = val;
+ val = dev_read_u32_default(dev, "max-frequency", -1); + if (val < 0) { + debug(" 'max-frequency' missing/invalid!\n"); + return -EINVAL; + } + priv->max_freq = val; + priv->min_freq = 400000; /* 400 kHz */
+ val = dev_read_u32_default(dev, "nexell,drive_dly", -1); + if (val < 0) { + debug(" 'nexell,drive_dly' missing/invalid!\n"); + return -EINVAL; + } + priv->d_delay = val;
+ val = dev_read_u32_default(dev, "nexell,drive_shift", -1); + if (val < 0) { + debug(" 'nexell,drive_shift' missing/invalid!\n"); + return -EINVAL; + } + priv->d_shift = val;
+ val = dev_read_u32_default(dev, "nexell,sample_dly", -1); + if (val < 0) { + debug(" 'nexell,sample_dly' missing/invalid!\n"); + return -EINVAL; + } + priv->s_delay = val;
+ val = dev_read_u32_default(dev, "nexell,sample_shift", -1); + if (val < 0) { + debug(" 'nexell,sample_shift' missing/invalid!\n"); + return -EINVAL; + } + priv->s_shift = val;
+ debug(" index==%d, name==%s, ioaddr==0x%08x, buswidth==%d, " + "fifo_size==%d, fifo_mode==%d, frequency==%d\n", + host->dev_index, host->name, (u32)host->ioaddr, + host->buswidth, priv->fifo_size, priv->fifo_mode, + priv->frequency); + debug(" min_freq==%d, max_freq==%d, delay: " + "0x%02x:0x%02x:0x%02x:0x%02x\n", + priv->min_freq, priv->max_freq, priv->d_delay, + priv->d_shift, priv->s_delay, priv->s_shift);
+ return 0; +}
+static int nexell_dwmmc_probe(struct udevice *dev) +{ + struct nexell_mmc_plat *plat = dev_get_platdata(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + struct udevice *pwr_dev __maybe_unused;
+ debug("%s():\n", __func__);
+ host->fifoth_val = MSIZE(0x2) | + RX_WMARK(priv->fifo_size / 2 - 1) | + TX_WMARK(priv->fifo_size / 2);
+ host->fifo_mode = priv->fifo_mode;
+ dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq); + host->mmc = &plat->mmc; + host->mmc->priv = &priv->host; + host->mmc->dev = dev; + upriv->mmc = host->mmc;
+ nx_dw_mmc_set_pin(host);
+ debug(" nx_dw_mmc_set_clk(host, frequency * 4 == %d)\n", + priv->frequency * 4); + nx_dw_mmc_set_clk(host, priv->frequency * 4);
+ nx_dw_mmc_reset(host->dev_index); + nx_dw_mmc_clk_delay(dev);
+ return dwmci_probe(dev); +}
+static int nexell_dwmmc_bind(struct udevice *dev) +{ + struct nexell_mmc_plat *plat = dev_get_platdata(dev);
+ return dwmci_bind(dev, &plat->mmc, &plat->cfg); +}
+static const struct udevice_id nexell_dwmmc_ids[] = { + { .compatible = "nexell,nexell-dwmmc" }, + { } +};
+U_BOOT_DRIVER(nexell_dwmmc_drv) = { + .name = "nexell_dwmmc", + .id = UCLASS_MMC, + .of_match = nexell_dwmmc_ids, + .ofdata_to_platdata = nexell_dwmmc_ofdata_to_platdata, + .ops = &dm_dwmci_ops, + .bind = nexell_dwmmc_bind, + .probe = nexell_dwmmc_probe, + .priv_auto_alloc_size = sizeof(struct nexell_dwmmc_priv), + .platdata_auto_alloc_size = sizeof(struct nexell_mmc_plat), +}; diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a837c35..b45aada 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o +obj-$(CONFIG_PWM_NX) += pwm-nexell.o diff --git a/drivers/pwm/pwm-nexell.c b/drivers/pwm/pwm-nexell.c new file mode 100644 index 0000000..6c0f8f4 --- /dev/null +++ b/drivers/pwm/pwm-nexell.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (C) 2011 Samsung Electronics
- Donghwa Lee dh09.lee@samsung.com
- */
+/* This codes are copied from arch/arm/cpu/armv7/s5p-common/pwm.c */
+#include <common.h> +#include <errno.h> +#include <pwm.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include "pwm-nexell.h"
+#if defined(CONFIG_ARCH_NEXELL) +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/tieoff.h>
+struct pwm_device { + int ch; + int grp; + int bit; + int pwm_fn; +};
+static struct pwm_device pwm_dev[] = { + [0] = { .ch = 0, .grp = 3, .bit = 1, .pwm_fn = 1 }, + [1] = { .ch = 1, .grp = 2, .bit = 13, .pwm_fn = 2 }, + [2] = { .ch = 2, .grp = 2, .bit = 14, .pwm_fn = 2 }, + [3] = { .ch = 3, .grp = 3, .bit = 0, .pwm_fn = 2 }, +}; +#endif
+int pwm_enable(int pwm_id) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long tcon;
+ tcon = readl(&pwm->tcon); + tcon |= TCON_START(pwm_id);
+ writel(tcon, &pwm->tcon);
+ return 0; +}
+void pwm_disable(int pwm_id) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long tcon;
+ tcon = readl(&pwm->tcon); + tcon &= ~TCON_START(pwm_id);
+ writel(tcon, &pwm->tcon); +}
+static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq) +{ + unsigned long tin_parent_rate; + unsigned int div; +#if defined(CONFIG_ARCH_NEXELL) + unsigned int pre_div; + const struct s5p_timer *pwm = + (struct s5p_timer *)PHY_BASEADDR_PWM; + unsigned int val; +#endif
+#if defined(CONFIG_ARCH_NEXELL) + struct clk *clk = clk_get(CORECLK_NAME_PCLK);
+ tin_parent_rate = clk_get_rate(clk); +#else + tin_parent_rate = get_pwm_clk(); +#endif
+#if defined(CONFIG_ARCH_NEXELL) + writel(0, &pwm->tcfg0); + val = readl(&pwm->tcfg0);
+ if (pwm_id < 2) + div = ((val >> 0) & 0xff) + 1; + else + div = ((val >> 8) & 0xff) + 1;
+ writel(0, &pwm->tcfg1); + val = readl(&pwm->tcfg1); + val = (val >> MUX_DIV_SHIFT(pwm_id)) & 0xF; + pre_div = (1UL << val);
+ freq = tin_parent_rate / div / pre_div;
+ return freq; +#else + for (div = 2; div <= 16; div *= 2) { + if ((tin_parent_rate / (div << 16)) < freq) + return tin_parent_rate / div; + }
+ return tin_parent_rate / 16; +#endif +}
+#define NS_IN_SEC 1000000000UL
+int pwm_config(int pwm_id, int duty_ns, int period_ns) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned int offset; + unsigned long tin_rate; + unsigned long tin_ns; + unsigned long frequency; + unsigned long tcon; + unsigned long tcnt; + unsigned long tcmp;
+ /* + * We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1GHz is easily representable + * by 32bits. + */ + if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0) + return -ERANGE;
+ if (duty_ns > period_ns) + return -EINVAL;
+ frequency = NS_IN_SEC / period_ns;
+ /* Check to see if we are changing the clock rate of the PWM */ + tin_rate = pwm_calc_tin(pwm_id, frequency);
+ tin_ns = NS_IN_SEC / tin_rate; +#if defined(CONFIG_ARCH_NEXELL) + /* The counter starts at zero. */ + tcnt = (period_ns / tin_ns) - 1; +#else + tcnt = period_ns / tin_ns; +#endif
+ /* Note, counters count down */ + tcmp = duty_ns / tin_ns; + tcmp = tcnt - tcmp;
+ /* Update the PWM register block. */ + offset = pwm_id * 3; + if (pwm_id < 4) { + writel(tcnt, &pwm->tcntb0 + offset); + writel(tcmp, &pwm->tcmpb0 + offset); + }
+ tcon = readl(&pwm->tcon); + tcon |= TCON_UPDATE(pwm_id); + if (pwm_id < 4) + tcon |= TCON_AUTO_RELOAD(pwm_id); + else + tcon |= TCON4_AUTO_RELOAD; + writel(tcon, &pwm->tcon);
+ tcon &= ~TCON_UPDATE(pwm_id); + writel(tcon, &pwm->tcon);
+ return 0; +}
+int pwm_init(int pwm_id, int div, int invert) +{ + u32 val; + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long ticks_per_period; + unsigned int offset, prescaler;
+ /* + * Timer Freq(HZ) = + * PWM_CLK / { (prescaler_value + 1) * (divider_value) } + */
+ val = readl(&pwm->tcfg0); + if (pwm_id < 2) { + prescaler = PRESCALER_0; + val &= ~0xff; + val |= (prescaler & 0xff); + } else { + prescaler = PRESCALER_1; + val &= ~(0xff << 8); + val |= (prescaler & 0xff) << 8; + } + writel(val, &pwm->tcfg0); + val = readl(&pwm->tcfg1); + val &= ~(0xf << MUX_DIV_SHIFT(pwm_id)); + val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id); + writel(val, &pwm->tcfg1);
+ if (pwm_id == 4) { + /* + * TODO(sjg): Use this as a countdown timer for now. We count + * down from the maximum value to 0, then reset. + */ + ticks_per_period = -1UL; + } else { + const unsigned long pwm_hz = 1000; +#if defined(CONFIG_ARCH_NEXELL) + struct clk *clk = clk_get(CORECLK_NAME_PCLK); + unsigned long timer_rate_hz = clk_get_rate(clk) / +#else + unsigned long timer_rate_hz = get_pwm_clk() / +#endif + ((prescaler + 1) * (1 << div));
+ ticks_per_period = timer_rate_hz / pwm_hz; + }
+ /* set count value */ + offset = pwm_id * 3;
+ writel(ticks_per_period, &pwm->tcntb0 + offset);
+ val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id)); + if (invert && pwm_id < 4) + val |= TCON_INVERTER(pwm_id); + writel(val, &pwm->tcon);
+ nx_gpio_set_pad_function(pwm_dev[pwm_id].grp, pwm_dev[pwm_id].bit, + pwm_dev[pwm_id].pwm_fn); + pwm_enable(pwm_id);
+ return 0; +} diff --git a/drivers/pwm/pwm-nexell.h b/drivers/pwm/pwm-nexell.h new file mode 100644 index 0000000..92dc707 --- /dev/null +++ b/drivers/pwm/pwm-nexell.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+
- Copyright (C) 2009 Samsung Electronics
- Kyungmin Park kyungmin.park@samsung.com
- Minkyu Kang mk7.kang@samsung.com
- */
+#ifndef __ASM_ARM_ARCH_PWM_H_ +#define __ASM_ARM_ARCH_PWM_H_
+#define PRESCALER_0 (8 - 1) /* prescaler of timer 0, 1 */ +#define PRESCALER_1 (16 - 1) /* prescaler of timer 2, 3, 4 */
+/* Divider MUX */ +#define MUX_DIV_1 0 /* 1/1 period */ +#define MUX_DIV_2 1 /* 1/2 period */ +#define MUX_DIV_4 2 /* 1/4 period */ +#define MUX_DIV_8 3 /* 1/8 period */ +#define MUX_DIV_16 4 /* 1/16 period */
+#define MUX_DIV_SHIFT(x) ((x) * 4)
+#define TCON_OFFSET(x) (((x) + 1) * (!!x) << 2)
+#define TCON_START(x) (1 << TCON_OFFSET(x)) +#define TCON_UPDATE(x) (1 << (TCON_OFFSET(x) + 1)) +#define TCON_INVERTER(x) (1 << (TCON_OFFSET(x) + 2)) +#define TCON_AUTO_RELOAD(x) (1 << (TCON_OFFSET(x) + 3)) +#define TCON4_AUTO_RELOAD (1 << 22)
+#ifndef __ASSEMBLY__ +struct s5p_timer { + unsigned int tcfg0; + unsigned int tcfg1; + unsigned int tcon; + unsigned int tcntb0; + unsigned int tcmpb0; + unsigned int tcnto0; + unsigned int tcntb1; + unsigned int tcmpb1; + unsigned int tcnto1; + unsigned int tcntb2; + unsigned int tcmpb2; + unsigned int tcnto2; + unsigned int tcntb3; + unsigned int res1; + unsigned int tcnto3; + unsigned int tcntb4; + unsigned int tcnto4; + unsigned int tintcstat; +}; +#endif /* __ASSEMBLY__ */
+#endif

Hello Stefan,
Am 20.02.2020 um 18:49 schrieb Stefan B.:
Hello Heiko,
see below my feedback, please give me further advise where indicated.
Unfortunately there have been some Bugs in the i2c-driver and I learned that this driver has not been used at all ("i2c-gpio" has been used instead). So I have done several Bugfixes and improvements appart from your proposals.
Regards Stefan
Am 04.02.20 um 07:58 schrieb Heiko Schocher:
Hello Stefan,
Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- i2c/nx_i2c.c: Some adaptions mainly because of changes in
"struct udevice".
- mmc: nexell_dw_mmc.c changed to nexell_dw_mmc_dm.c (switched to DM).
Signed-off-by: Stefan Bosch stefan_b@posteo.net
drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/nx_gpio.c | 252 +++++++++++++++++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/nx_i2c.c | 537 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/nexell_dw_mmc_dm.c | 350 +++++++++++++++++++++++++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-nexell.c | 252 +++++++++++++++++++ drivers/pwm/pwm-nexell.h | 54 +++++
Could you please split this patch into 4 parts (i2c, gpio, mmc and pwm) ?
Thanks!
Ok, I will split this patch.
Thanks!
12 files changed, 1473 insertions(+) create mode 100644 drivers/gpio/nx_gpio.c create mode 100644 drivers/i2c/nx_i2c.c create mode 100644 drivers/mmc/nexell_dw_mmc_dm.c create mode 100644 drivers/pwm/pwm-nexell.c create mode 100644 drivers/pwm/pwm-nexell.h
[...]
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 449046b..e3340de 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o +obj-$(CONFIG_NX_GPIO) += nx_gpio.o
Please keep lists sorted.
The list is not sorted (at least in no alphabetical order), but I can e.g. move "... += nx_gpio.o" one line up?
Find for me.
diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c new file mode 100644 index 0000000..86472f6 --- /dev/null +++ b/drivers/gpio/nx_gpio.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- DeokJin, Lee truevirtue@nexell.co.kr
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/gpio.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_gpio_regs { + u32 data; /* Data register */ + u32 outputenb; /* Output Enable register */ + u32 detmode[2]; /* Detect Mode Register */ + u32 intenb; /* Interrupt Enable Register */ + u32 det; /* Event Detect Register */ + u32 pad; /* Pad Status Register */ +};
+struct nx_alive_gpio_regs { + u32 pwrgate; /* Power Gating Register */ + u32 reserved0[28]; /* Reserved0 */ + u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */ + u32 outputenb; /* Alive GPIO Output Enable Register */ + u32 outputenb_read; /* Alive GPIO Output Read Register */ + u32 reserved1[3]; /* Reserved1 */ + u32 pad_reset; /* Alive GPIO Output Reset Register */ + u32 data; /* Alive GPIO Output Register */ + u32 pad_read; /* Alive GPIO Pad Read Register */ + u32 reserved2[33]; /* Reserved2 */ + u32 pad; /* Alive GPIO Input Value Register */ +};
+struct nx_gpio_platdata { + void *regs; + int gpio_count; + const char *bank_name; +};
+static int nx_alive_gpio_is_check(struct udevice *dev) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + const char *bank_name = plat->bank_name;
+ if (!strcmp(bank_name, "gpio_alv")) + return 1;
+ return 0; +}
+static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ setbits_le32(®s->outputenb_reset, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + setbits_le32(®s->pad_reset, 1 << pin);
+ setbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_get_value(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + unsigned int mask = 1UL << pin; + unsigned int value;
+ value = (readl(®s->pad_read) & mask) >> pin;
+ return value; +}
+static int nx_alive_gpio_set_value(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs;
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->pad_reset, 1 << pin);
+ return 0; +}
+static int nx_alive_gpio_get_function(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_alive_gpio_regs *const regs = plat->regs; + unsigned int mask = (1UL << pin); + unsigned int output;
+ output = readl(®s->outputenb_read) & mask;
+ if (output) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +}
+static int nx_gpio_direction_input(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_direction_input(dev, pin);
+ clrbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_gpio_direction_output(struct udevice *dev, unsigned int pin, + int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_direction_output(dev, pin, val);
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin);
+ setbits_le32(®s->outputenb, 1 << pin);
+ return 0; +}
+static int nx_gpio_get_value(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + unsigned int mask = 1UL << pin; + unsigned int value;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_get_value(dev, pin);
+ value = (readl(®s->pad) & mask) >> pin;
+ return value; +}
+static int nx_gpio_set_value(struct udevice *dev, unsigned int pin, int val) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_set_value(dev, pin, val);
+ if (val) + setbits_le32(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin);
+ return 0; +}
+static int nx_gpio_get_function(struct udevice *dev, unsigned int pin) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev); + struct nx_gpio_regs *const regs = plat->regs; + unsigned int mask = (1UL << pin); + unsigned int output;
+ if (nx_alive_gpio_is_check(dev)) + return nx_alive_gpio_get_function(dev, pin);
+ output = readl(®s->outputenb) & mask;
+ if (output) + return GPIOF_OUTPUT; + else + return GPIOF_INPUT; +}
+static int nx_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+ uc_priv->gpio_count = plat->gpio_count; + uc_priv->bank_name = plat->bank_name;
+ return 0; +}
+static int nx_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+ plat->regs = map_physmem(devfdt_get_addr(dev), + sizeof(struct nx_gpio_regs), + MAP_NOCACHE); + plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->node.of_offset, + "nexell,gpio-bank-width", 32); + plat->bank_name = fdt_getprop(gd->fdt_blob, dev->node.of_offset, + "gpio-bank-name", NULL);
+ return 0; +}
+static const struct dm_gpio_ops nx_gpio_ops = { + .direction_input = nx_gpio_direction_input, + .direction_output = nx_gpio_direction_output, + .get_value = nx_gpio_get_value, + .set_value = nx_gpio_set_value, + .get_function = nx_gpio_get_function, +};
+static const struct udevice_id nx_gpio_ids[] = { + { .compatible = "nexell,nexell-gpio" }, + { } +};
+U_BOOT_DRIVER(nx_gpio) = { + .name = "nx_gpio", + .id = UCLASS_GPIO, + .of_match = nx_gpio_ids, + .ops = &nx_gpio_ops, + .ofdata_to_platdata = nx_gpio_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct nx_gpio_platdata), + .probe = nx_gpio_probe, +}; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 03d2fed..2cd0ed3 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -317,6 +317,15 @@ config SYS_MXC_I2C8_SLAVE MXC I2C8 Slave endif +config SYS_I2C_NEXELL + bool "Nexell I2C driver" + depends on DM_I2C + help + Add support for the Nexell I2C driver. This is used with various + Nexell parts such as S5Pxx18 series SoCs. All chips + have several I2C ports and all are provided, controlled by the + device tree.
config SYS_I2C_OMAP24XX bool "TI OMAP2+ I2C driver" depends on ARCH_OMAP2PLUS || ARCH_K3 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f5a471f..64b8ead 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o +obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o diff --git a/drivers/i2c/nx_i2c.c b/drivers/i2c/nx_i2c.c new file mode 100644 index 0000000..a3eec6c --- /dev/null +++ b/drivers/i2c/nx_i2c.c @@ -0,0 +1,537 @@ +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/clk.h> +#include <asm/arch/nx_gpio.h>
+#define I2C_WRITE 0 +#define I2C_READ 1
+#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */
+#define I2CLC_FILTER 0x04 /* SDA filter on*/ +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CSTAT_ABT 0x08 /* Arbitration bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRENB 0x20 /* Interrupt Enable bit */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
+#define I2C_TIMEOUT_MS 10 /* 10 ms */
+#define I2C_M_NOSTOP 0x100
+#ifndef CONFIG_MAX_I2C_NUM +#define CONFIG_MAX_I2C_NUM 3 +#endif
Is this really configurable? If so, I do not find the Kconfig description.
No, it is not configurable. I have changed it to MAX_I2C_NUM.
Ok.
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_i2c_regs { + uint iiccon; + uint iicstat; + uint iicadd; + uint iicds; + uint iiclc; +};
+struct nx_i2c_bus { + uint bus_num; + struct nx_i2c_regs *regs; + uint speed; + uint target_speed; + uint sda_delay; +};
+/* s5pxx18 i2c must be reset before enabled */ +static void i2c_reset(int ch) +{ + int rst_id = RESET_ID_I2C0 + ch;
+ nx_rstcon_setrst(rst_id, 0); + nx_rstcon_setrst(rst_id, 1); +}
+/* FIXME : this func will be removed after reset dm driver ported.
- set mmc pad alternative func.
- */
+static void set_i2c_pad_func(struct nx_i2c_bus *i2c) +{ + switch (i2c->bus_num) { + case 0: + nx_gpio_set_pad_function(3, 2, 1); + nx_gpio_set_pad_function(3, 3, 1); + break; + case 1: + nx_gpio_set_pad_function(3, 4, 1); + nx_gpio_set_pad_function(3, 5, 1); + break; + case 2: + nx_gpio_set_pad_function(3, 6, 1); + nx_gpio_set_pad_function(3, 7, 1); + break; + } +}
Hmm... may this should be moved into a seperate pincontrol driver?
According to the above FIXME comment from Nexell it probably should. But there is no pincontrol driver implemented. But is the change to a driver necessary for now?
Hmm.. it would be better to have a pinctrl driver, but I can accept it for now.
+static uint i2c_get_clkrate(struct nx_i2c_bus *bus) +{ + struct clk *clk; + int index = bus->bus_num; + char name[50] = {0, };
?
+ sprintf(name, "%s.%d", DEV_NAME_I2C, index);
Where is DEV_NAME_I2C defined ?
DEV_NAME_I2C is defined in arch/arm/mach-nexell/include/mach/nexell.h
Ah, ok.
+ clk = clk_get((const char *)name); + if (!clk) + return -1;
+ return clk_get_rate(clk); +}
+static uint i2c_set_clk(struct nx_i2c_bus *bus, uint enb) +{ + struct clk *clk; + char name[50];
+ sprintf(name, "%s.%d", DEV_NAME_I2C, bus->bus_num); + clk = clk_get((const char *)name); + if (!clk) + return -1;
+ if (enb) { + clk_disable(clk); + clk_enable(clk); + } else { + clk_disable(clk); + }
+ return 0; +}
+/* get i2c module number from base address */ +static uint i2c_get_busnum(struct nx_i2c_bus *bus) +{ + void *base_addr = (void *)PHY_BASEADDR_I2C0; + int i;
+ for (i = 0; i < CONFIG_MAX_I2C_NUM; i++) { + if (base_addr == ((void *)bus->regs)) { + bus->bus_num = i; + return i; + } + base_addr += 0x1000; + }
+ return -1;
return -ENODEV;
Hmm... is there no chance to use seq from struct udevice
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/dm/device.h#L152
?
For example like: https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/mxc_i2c.c#L895
Ok, I have changed this as proposed.
Thanks!
+}
+/* Set SDA line delay */ +static int nx_i2c_set_sda_delay(struct nx_i2c_bus *bus, ulong clkin) +{ + struct nx_i2c_regs *i2c = bus->regs; + uint sda_delay = 0;
+ if (bus->sda_delay) { + sda_delay = clkin * bus->sda_delay; + sda_delay = DIV_ROUND_UP(sda_delay, 1000000); + sda_delay = DIV_ROUND_UP(sda_delay, 5); + if (sda_delay > 3) + sda_delay = 3; + sda_delay |= I2CLC_FILTER; + } else { + sda_delay = 0; + }
+ sda_delay &= 0x7; + writel(sda_delay, &i2c->iiclc);
+ return 0; +}
+/* Calculate the value of the divider and prescaler, set the bus speed. */ +static int nx_i2c_set_bus_speed(struct udevice *dev, uint speed) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + struct nx_i2c_regs *i2c = bus->regs; + unsigned long freq, pres = 16, div;
+ freq = i2c_get_clkrate(bus); + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512;
+ div = 0; + while ((freq / pres / (div + 1)) > speed) + div++;
+ /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + writel((div & 0x0F) | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
+ /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(0x00, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
+ bus->speed = bus->target_speed / (div * pres);
Do you want to allow all values of speeds or may you want to use standard speeds, see:
https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L33
I'd like to allow all values of speed. In my opinion allowing only standard speeds does complicate things (e.g. how to do error handling?). Furthermore I think sometimes it could be handy to be able to set speed to an arbitrary value (e.g. to a lower value than 100000) when trying a new i2c-device on the bus.
ok.
+ return 0; +}
+static void nx_i2c_set_clockrate(struct udevice *dev, uint speed) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + ulong clkin;
+ nx_i2c_set_bus_speed(dev, speed); + clkin = bus->speed; /* the actual i2c speed */ + clkin /= 1000; /* clkin now in Khz */ + nx_i2c_set_sda_delay(bus, clkin); +}
+static void i2c_process_node(struct udevice *dev) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob;
+ int node;
+ node = dev->node.of_offset;
+ bus->target_speed = fdtdec_get_int(blob, node, + "nexell,i2c-max-bus-freq", 0); + bus->sda_delay = fdtdec_get_int(blob, node, + "nexell,i2c-sda-delay", 0);
You introdue here new properties, please document them in u-boot:/doc/device-tree-bindings/i2c
Please without "nexell,"
I have changed "nexell,i2c-max-bus-freq" to the already defined "clock-frequency". Furthermore I have changed "nexell,i2c-sda-delay" to "i2c-sda-delay-ns". Furthermore, I have added "nx_i2c.txt" in doc/device-tree-bindings/i2c.
Thanks!
Do you plan to post also a linux i2c driver?
If so, the devicetree bindings should be discussed there to avoid different properties between U-Boot and linux!
No, I do not plan to post a linux driver.
Ok.
+}
+static int nx_i2c_probe(struct udevice *dev) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev);
+ /* get regs */ + bus->regs = (struct nx_i2c_regs *)devfdt_get_addr(dev); + /* calc index */ + if (!i2c_get_busnum(bus)) { + debug("not found i2c number!\n"); + return -1;
please return -ENODEV
Ok
+ }
+ /* i2c optional node parsing */ + i2c_process_node(dev); + if (!bus->target_speed) + return -1;
please return here also an errorcode from include/linux/errno.h
Ok
Hmm.. if you return here if target_speed is not set, it is not optional!
You are right, I have removed 'optional' in the comment.
Thanks.
+ /* reset */ + i2c_reset(bus->bus_num); + /* gpio pad */ + set_i2c_pad_func(bus);
+ /* clock rate */ + i2c_set_clk(bus, 1); + nx_i2c_set_clockrate(dev, bus->target_speed); + i2c_set_clk(bus, 0);
+ return 0; +}
+/* i2c bus busy check */ +static int i2c_is_busy(struct nx_i2c_regs *i2c) +{ + ulong start_time;
+ start_time = get_timer(0); + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) { + debug("Timeout\n"); + return -I2C_NOK_TOUT; + } + } + return 0; +}
+/* irq enable/disable functions */ +static void i2c_enable_irq(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ reg = readl(&i2c->iiccon); + reg |= I2CCON_IRENB; + writel(reg, &i2c->iiccon); +}
+/* irq clear function */ +static void i2c_clear_irq(struct nx_i2c_regs *i2c) +{ + clrbits_le32(&i2c->iiccon, I2CCON_IRPND); +}
+/* ack enable functions */ +static void i2c_enable_ack(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ reg = readl(&i2c->iiccon); + reg |= I2CCON_ACKGEN; + writel(reg, &i2c->iiccon); +}
+static void i2c_send_stop(struct nx_i2c_regs *i2c) +{ + unsigned int reg;
+ /* Send STOP. */ + reg = readl(&i2c->iicstat); + reg |= I2C_MODE_MR | I2C_TXRX_ENA; + reg &= (~I2C_START_STOP); + writel(reg, &i2c->iicstat); + i2c_clear_irq(i2c); +}
+static int wait_for_xfer(struct nx_i2c_regs *i2c) +{ + unsigned long start_time = get_timer(0);
+ do { + if (readl(&i2c->iiccon) & I2CCON_IRPND) + return (readl(&i2c->iicstat) & I2CSTAT_NACK) ? + I2C_NACK : I2C_OK; + } while (get_timer(start_time) < I2C_TIMEOUT_MS);
+ return I2C_NOK_TOUT; +}
+static int i2c_transfer(struct nx_i2c_regs *i2c, + uchar cmd_type, + uchar chip, + uchar addr[], + uchar addr_len, + uchar data[], + unsigned short data_len, + uint seq) +{ + uint status; + int i = 0, result;
+ if (data == 0 || data_len == 0) { + /*Don't support data transfer of no length or to address 0 */ + debug("%s: bad call\n", __func__); + return I2C_NOK; + }
+ i2c_enable_irq(i2c); + i2c_enable_ack(i2c);
+ /* Get the slave chip address going */ + writel(chip, &i2c->iicds); + status = I2C_TXRX_ENA | I2C_START_STOP; + if (cmd_type == I2C_WRITE || (addr && addr_len)) + status |= I2C_MODE_MT; + else + status |= I2C_MODE_MR; + writel(status, &i2c->iicstat); + if (seq) + i2c_clear_irq(i2c);
+ /* Wait for chip address to transmit. */ + result = wait_for_xfer(i2c); + if (result != I2C_OK) + goto bailout;
+ /* If register address needs to be transmitted - do it now. */ + if (addr && addr_len) { /* register addr */ + while ((i < addr_len) && (result == I2C_OK)) { + writel(addr[i++], &i2c->iicds); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + }
+ i = 0; + if (result != I2C_OK) + goto bailout; + }
+ switch (cmd_type) { + case I2C_WRITE: + while ((i < data_len) && (result == I2C_OK)) { + writel(data[i++], &i2c->iicds); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + } + break; + case I2C_READ: + if (addr && addr_len) { + /* + * Register address has been sent, now send slave chip + * address again to start the actual read transaction. + */ + writel(chip, &i2c->iicds);
+ /* Generate a re-START. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA + | I2C_START_STOP, &i2c->iicstat); + i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + if (result != I2C_OK) + goto bailout; + }
+ while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + clrbits_le32(&i2c->iiccon + , I2CCON_ACKGEN);
+ i2c_clear_irq(i2c); + result = wait_for_xfer(i2c); + data[i++] = readb(&i2c->iicds); + }
+ if (result == I2C_NACK) + result = I2C_OK; /* Normal terminated read. */ + break;
+ default: + debug("%s: bad call\n", __func__); + result = I2C_NOK; + break; + }
+bailout: + return result; +}
+static int nx_i2c_read(struct udevice *dev, uchar chip, uint addr, + uint alen, uchar *buffer, uint len, uint seq) +{ + struct nx_i2c_bus *i2c; + uchar xaddr[4]; + int ret;
+ i2c = dev_get_priv(dev); + if (!i2c) + return -EFAULT;
+ if (alen > 4) { + debug("I2C read: addr len %d not supported\n", alen); + return -EADDRNOTAVAIL; + }
+ if (alen > 0) + xaddr[0] = (addr >> 24) & 0xFF;
+ if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + }
+ ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, + &xaddr[4 - alen], alen, buffer, len, seq);
+ if (ret) { + debug("I2C read failed %d\n", ret); + return -EIO; + }
+ return 0; +}
+static int nx_i2c_write(struct udevice *dev, uchar chip, uint addr, + uint alen, uchar *buffer, uint len, uint seq) +{ + struct nx_i2c_bus *i2c; + uchar xaddr[4]; + int ret;
+ i2c = dev_get_priv(dev); + if (!i2c) + return -EFAULT;
+ if (alen > 4) { + debug("I2C write: addr len %d not supported\n", alen); + return -EINVAL; + }
+ if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + }
+ ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, + &xaddr[4 - alen], alen, buffer, len, seq); + if (ret) { + debug("I2C write failed %d\n", ret); + return -EIO; + }
+ return 0; +}
+static int nx_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + struct nx_i2c_bus *bus = dev_get_priv(dev); + struct nx_i2c_regs *i2c = bus->regs; + int ret; + int i;
+ /* The power loss by the clock, only during on/off. */ + i2c_set_clk(bus, 1);
+ /* Bus State(Busy) check */ + ret = i2c_is_busy(i2c); + if (ret < 0) + return ret;
+ for (i = 0; i < nmsgs; msg++, i++) { + if (msg->flags & I2C_M_RD) { + ret = nx_i2c_read(dev, msg->addr, 0, 0, msg->buf, + msg->len, i); + } else { + ret = nx_i2c_write(dev, msg->addr, 0, 0, msg->buf, + msg->len, i); + }
+ if (ret) { + debug("i2c_xfer: error sending\n"); + return -EREMOTEIO; + } + } + /* Send Stop */ + i2c_send_stop(i2c); + i2c_set_clk(bus, 0);
+ return ret ? -EREMOTEIO : 0; +};
+static const struct dm_i2c_ops nx_i2c_ops = { + .xfer = nx_i2c_xfer, + .set_bus_speed = nx_i2c_set_bus_speed, +};
+static const struct udevice_id nx_i2c_ids[] = { + { .compatible = "nexell,s5pxx18-i2c" },
Same here as for the new properties. Please discuss the names on
devicetree@vger.kernel.org devicetree@vger.kernel.org
first!
+ { } +};
+U_BOOT_DRIVER(i2c_nexell) = { + .name = "i2c_nexell", + .id = UCLASS_I2C, + .of_match = nx_i2c_ids, + .probe = nx_i2c_probe, + .priv_auto_alloc_size = sizeof(struct nx_i2c_bus), + .ops = &nx_i2c_ops, +};
bye, Heiko
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb8e7c0 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -253,6 +253,12 @@ config MMC_DW_SNPS This selects support for Synopsys DesignWare Memory Card Interface driver extensions used in various Synopsys ARC devboards. +config NEXELL_DWMMC + bool "Nexell SD/MMC controller support" + depends on ARCH_NEXELL + depends on MMC_DW + default y
config MMC_MESON_GX bool "Meson GX EMMC controller support" depends on DM_MMC && BLK && ARCH_MESON diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..a7b5a7b 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o +obj-$(CONFIG_NEXELL_DWMMC) += nexell_dw_mmc_dm.o # SDHCI obj-$(CONFIG_MMC_SDHCI) += sdhci.o diff --git a/drivers/mmc/nexell_dw_mmc_dm.c b/drivers/mmc/nexell_dw_mmc_dm.c new file mode 100644 index 0000000..b06b60d --- /dev/null +++ b/drivers/mmc/nexell_dw_mmc_dm.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- Youngbok, Park park@nexell.co.kr
- (C) Copyright 2019 Stefan Bosch stefan_b@posteo.net
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-structs.h> +#include <dwmmc.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/reset.h>
+#define DWMCI_CLKSEL 0x09C +#define DWMCI_SHIFT_0 0x0 +#define DWMCI_SHIFT_1 0x1 +#define DWMCI_SHIFT_2 0x2 +#define DWMCI_SHIFT_3 0x3 +#define DWMCI_SET_SAMPLE_CLK(x) (x) +#define DWMCI_SET_DRV_CLK(x) ((x) << 16) +#define DWMCI_SET_DIV_RATIO(x) ((x) << 24) +#define DWMCI_CLKCTRL 0x114 +#define NX_MMC_CLK_DELAY(x, y, a, b) ((((x) & 0xFF) << 0) |\ + (((y) & 0x03) << 16) |\ + (((a) & 0xFF) << 8) |\ + (((b) & 0x03) << 24))
+struct nexell_mmc_plat { + struct mmc_config cfg; + struct mmc mmc; +};
+struct nexell_dwmmc_priv { + struct clk *clk; + struct dwmci_host host; + int fifo_size; + bool fifo_mode; + int frequency; + u32 min_freq; + u32 max_freq; + int d_delay; + int d_shift; + int s_delay; + int s_shift;
+};
+struct clk *clk_get(const char *id);
+static void set_pin_stat(int index, int bit, int value) +{ +#if !defined(CONFIG_SPL_BUILD) + nx_gpio_set_pad_function(index, bit, value); +#else +#if defined(CONFIG_ARCH_S5P4418) || \ + defined(CONFIG_ARCH_S5P6818)
+ unsigned long base[5] = { + PHY_BASEADDR_GPIOA, PHY_BASEADDR_GPIOB, + PHY_BASEADDR_GPIOC, PHY_BASEADDR_GPIOD, + PHY_BASEADDR_GPIOE, + };
+ dw_mmc_set_pin(base[index], bit, value); +#endif +#endif +}
+static void nx_dw_mmc_set_pin(struct dwmci_host *host) +{ + debug(" %s(): dev_index == %d", __func__, host->dev_index);
+ switch (host->dev_index) { + case 0: + set_pin_stat(0, 29, 1); + set_pin_stat(0, 31, 1); + set_pin_stat(1, 1, 1); + set_pin_stat(1, 3, 1); + set_pin_stat(1, 5, 1); + set_pin_stat(1, 7, 1); + break; + case 1: + set_pin_stat(3, 22, 1); + set_pin_stat(3, 23, 1); + set_pin_stat(3, 24, 1); + set_pin_stat(3, 25, 1); + set_pin_stat(3, 26, 1); + set_pin_stat(3, 27, 1); + break; + case 2: + set_pin_stat(2, 18, 2); + set_pin_stat(2, 19, 2); + set_pin_stat(2, 20, 2); + set_pin_stat(2, 21, 2); + set_pin_stat(2, 22, 2); + set_pin_stat(2, 23, 2); + if (host->buswidth == 8) { + set_pin_stat(4, 21, 2); + set_pin_stat(4, 22, 2); + set_pin_stat(4, 23, 2); + set_pin_stat(4, 24, 2); + } + break; + default: + debug(" is invalid!"); + } + debug("\n"); +}
+static void nx_dw_mmc_clksel(struct dwmci_host *host) +{ + u32 val;
+#ifdef CONFIG_BOOST_MMC + val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | + DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(1); +#else + val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | + DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(3); +#endif
+ dwmci_writel(host, DWMCI_CLKSEL, val); +}
+static void nx_dw_mmc_reset(int ch) +{ + int rst_id = RESET_ID_SDMMC0 + ch;
+ nx_rstcon_setrst(rst_id, 0); + nx_rstcon_setrst(rst_id, 1); +}
+static void nx_dw_mmc_clk_delay(struct udevice *dev) +{ + unsigned int delay; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host;
+ delay = NX_MMC_CLK_DELAY(priv->d_delay, + priv->d_shift, priv->s_delay, priv->s_shift);
+ writel(delay, (host->ioaddr + DWMCI_CLKCTRL)); + debug("%s(): Values set: d_delay==%d, d_shift==%d, s_delay==%d, " + "s_shift==%d\n", __func__, priv->d_delay, priv->d_shift, + priv->s_delay, priv->s_shift); +}
+static unsigned int nx_dw_mmc_get_clk(struct dwmci_host *host, uint freq) +{ + struct clk *clk; + struct udevice *dev = host->priv; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+ int index = host->dev_index; + char name[50] = { 0, };
+ clk = priv->clk; + if (!clk) { + sprintf(name, "%s.%d", DEV_NAME_SDHC, index); + clk = clk_get((const char *)name); + if (!clk) + return 0; + priv->clk = clk; + }
+ return clk_get_rate(clk) / 2; +}
+static unsigned long nx_dw_mmc_set_clk(struct dwmci_host *host, + unsigned int rate) +{ + struct clk *clk; + char name[50] = { 0, }; + struct udevice *dev = host->priv; + struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+ int index = host->dev_index;
+ clk = priv->clk; + if (!clk) { + sprintf(name, "%s.%d", DEV_NAME_SDHC, index); + clk = clk_get((const char *)name); + if (!clk) + return 0; + priv->clk = clk; + }
+ clk_disable(clk); + rate = clk_set_rate(clk, rate); + clk_enable(clk);
+ return rate; +}
+static int nexell_dwmmc_ofdata_to_platdata(struct udevice *dev) +{ + /* if (dev): *priv = dev->priv, else: *priv = NULL */ + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + int val = -1;
+ debug("%s()\n", __func__);
+ host->name = dev->name; + host->ioaddr = dev_read_addr_ptr(dev);
+ val = dev_read_u32_default(dev, "nexell,bus-width", -1); + if (val < 0) { + debug(" 'nexell,bus-width' missing/invalid!\n"); + return -EINVAL; + } + host->buswidth = val; + host->get_mmc_clk = nx_dw_mmc_get_clk; + host->clksel = nx_dw_mmc_clksel; + host->priv = dev;
+ val = dev_read_u32_default(dev, "index", -1); + if (val < 0) { + debug(" 'index' missing/invalid!\n"); + return -EINVAL; + } + host->dev_index = val;
+ val = dev_read_u32_default(dev, "fifo-size", 0x20); + if (val <= 0) { + debug(" 'fifo-size' missing/invalid!\n"); + return -EINVAL; + } + priv->fifo_size = val;
+ priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
+ val = dev_read_u32_default(dev, "frequency", -1); + if (val < 0) { + debug(" 'frequency' missing/invalid!\n"); + return -EINVAL; + } + priv->frequency = val;
+ val = dev_read_u32_default(dev, "max-frequency", -1); + if (val < 0) { + debug(" 'max-frequency' missing/invalid!\n"); + return -EINVAL; + } + priv->max_freq = val; + priv->min_freq = 400000; /* 400 kHz */
+ val = dev_read_u32_default(dev, "nexell,drive_dly", -1); + if (val < 0) { + debug(" 'nexell,drive_dly' missing/invalid!\n"); + return -EINVAL; + } + priv->d_delay = val;
+ val = dev_read_u32_default(dev, "nexell,drive_shift", -1); + if (val < 0) { + debug(" 'nexell,drive_shift' missing/invalid!\n"); + return -EINVAL; + } + priv->d_shift = val;
+ val = dev_read_u32_default(dev, "nexell,sample_dly", -1); + if (val < 0) { + debug(" 'nexell,sample_dly' missing/invalid!\n"); + return -EINVAL; + } + priv->s_delay = val;
+ val = dev_read_u32_default(dev, "nexell,sample_shift", -1); + if (val < 0) { + debug(" 'nexell,sample_shift' missing/invalid!\n"); + return -EINVAL; + } + priv->s_shift = val;
+ debug(" index==%d, name==%s, ioaddr==0x%08x, buswidth==%d, " + "fifo_size==%d, fifo_mode==%d, frequency==%d\n", + host->dev_index, host->name, (u32)host->ioaddr, + host->buswidth, priv->fifo_size, priv->fifo_mode, + priv->frequency); + debug(" min_freq==%d, max_freq==%d, delay: " + "0x%02x:0x%02x:0x%02x:0x%02x\n", + priv->min_freq, priv->max_freq, priv->d_delay, + priv->d_shift, priv->s_delay, priv->s_shift);
+ return 0; +}
+static int nexell_dwmmc_probe(struct udevice *dev) +{ + struct nexell_mmc_plat *plat = dev_get_platdata(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct nexell_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + struct udevice *pwr_dev __maybe_unused;
+ debug("%s():\n", __func__);
+ host->fifoth_val = MSIZE(0x2) | + RX_WMARK(priv->fifo_size / 2 - 1) | + TX_WMARK(priv->fifo_size / 2);
+ host->fifo_mode = priv->fifo_mode;
+ dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq); + host->mmc = &plat->mmc; + host->mmc->priv = &priv->host; + host->mmc->dev = dev; + upriv->mmc = host->mmc;
+ nx_dw_mmc_set_pin(host);
+ debug(" nx_dw_mmc_set_clk(host, frequency * 4 == %d)\n", + priv->frequency * 4); + nx_dw_mmc_set_clk(host, priv->frequency * 4);
+ nx_dw_mmc_reset(host->dev_index); + nx_dw_mmc_clk_delay(dev);
+ return dwmci_probe(dev); +}
+static int nexell_dwmmc_bind(struct udevice *dev) +{ + struct nexell_mmc_plat *plat = dev_get_platdata(dev);
+ return dwmci_bind(dev, &plat->mmc, &plat->cfg); +}
+static const struct udevice_id nexell_dwmmc_ids[] = { + { .compatible = "nexell,nexell-dwmmc" }, + { } +};
+U_BOOT_DRIVER(nexell_dwmmc_drv) = { + .name = "nexell_dwmmc", + .id = UCLASS_MMC, + .of_match = nexell_dwmmc_ids, + .ofdata_to_platdata = nexell_dwmmc_ofdata_to_platdata, + .ops = &dm_dwmci_ops, + .bind = nexell_dwmmc_bind, + .probe = nexell_dwmmc_probe, + .priv_auto_alloc_size = sizeof(struct nexell_dwmmc_priv), + .platdata_auto_alloc_size = sizeof(struct nexell_mmc_plat), +}; diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a837c35..b45aada 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o +obj-$(CONFIG_PWM_NX) += pwm-nexell.o diff --git a/drivers/pwm/pwm-nexell.c b/drivers/pwm/pwm-nexell.c new file mode 100644 index 0000000..6c0f8f4 --- /dev/null +++ b/drivers/pwm/pwm-nexell.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (C) 2011 Samsung Electronics
- Donghwa Lee dh09.lee@samsung.com
- */
+/* This codes are copied from arch/arm/cpu/armv7/s5p-common/pwm.c */
+#include <common.h> +#include <errno.h> +#include <pwm.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include "pwm-nexell.h"
+#if defined(CONFIG_ARCH_NEXELL) +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/tieoff.h>
+struct pwm_device { + int ch; + int grp; + int bit; + int pwm_fn; +};
+static struct pwm_device pwm_dev[] = { + [0] = { .ch = 0, .grp = 3, .bit = 1, .pwm_fn = 1 }, + [1] = { .ch = 1, .grp = 2, .bit = 13, .pwm_fn = 2 }, + [2] = { .ch = 2, .grp = 2, .bit = 14, .pwm_fn = 2 }, + [3] = { .ch = 3, .grp = 3, .bit = 0, .pwm_fn = 2 }, +}; +#endif
+int pwm_enable(int pwm_id) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long tcon;
+ tcon = readl(&pwm->tcon); + tcon |= TCON_START(pwm_id);
+ writel(tcon, &pwm->tcon);
+ return 0; +}
+void pwm_disable(int pwm_id) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long tcon;
+ tcon = readl(&pwm->tcon); + tcon &= ~TCON_START(pwm_id);
+ writel(tcon, &pwm->tcon); +}
+static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq) +{ + unsigned long tin_parent_rate; + unsigned int div; +#if defined(CONFIG_ARCH_NEXELL) + unsigned int pre_div; + const struct s5p_timer *pwm = + (struct s5p_timer *)PHY_BASEADDR_PWM; + unsigned int val; +#endif
+#if defined(CONFIG_ARCH_NEXELL) + struct clk *clk = clk_get(CORECLK_NAME_PCLK);
+ tin_parent_rate = clk_get_rate(clk); +#else + tin_parent_rate = get_pwm_clk(); +#endif
+#if defined(CONFIG_ARCH_NEXELL) + writel(0, &pwm->tcfg0); + val = readl(&pwm->tcfg0);
+ if (pwm_id < 2) + div = ((val >> 0) & 0xff) + 1; + else + div = ((val >> 8) & 0xff) + 1;
+ writel(0, &pwm->tcfg1); + val = readl(&pwm->tcfg1); + val = (val >> MUX_DIV_SHIFT(pwm_id)) & 0xF; + pre_div = (1UL << val);
+ freq = tin_parent_rate / div / pre_div;
+ return freq; +#else + for (div = 2; div <= 16; div *= 2) { + if ((tin_parent_rate / (div << 16)) < freq) + return tin_parent_rate / div; + }
+ return tin_parent_rate / 16; +#endif +}
+#define NS_IN_SEC 1000000000UL
+int pwm_config(int pwm_id, int duty_ns, int period_ns) +{ + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned int offset; + unsigned long tin_rate; + unsigned long tin_ns; + unsigned long frequency; + unsigned long tcon; + unsigned long tcnt; + unsigned long tcmp;
+ /* + * We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1GHz is easily representable + * by 32bits. + */ + if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0) + return -ERANGE;
+ if (duty_ns > period_ns) + return -EINVAL;
+ frequency = NS_IN_SEC / period_ns;
+ /* Check to see if we are changing the clock rate of the PWM */ + tin_rate = pwm_calc_tin(pwm_id, frequency);
+ tin_ns = NS_IN_SEC / tin_rate; +#if defined(CONFIG_ARCH_NEXELL) + /* The counter starts at zero. */ + tcnt = (period_ns / tin_ns) - 1; +#else + tcnt = period_ns / tin_ns; +#endif
+ /* Note, counters count down */ + tcmp = duty_ns / tin_ns; + tcmp = tcnt - tcmp;
+ /* Update the PWM register block. */ + offset = pwm_id * 3; + if (pwm_id < 4) { + writel(tcnt, &pwm->tcntb0 + offset); + writel(tcmp, &pwm->tcmpb0 + offset); + }
+ tcon = readl(&pwm->tcon); + tcon |= TCON_UPDATE(pwm_id); + if (pwm_id < 4) + tcon |= TCON_AUTO_RELOAD(pwm_id); + else + tcon |= TCON4_AUTO_RELOAD; + writel(tcon, &pwm->tcon);
+ tcon &= ~TCON_UPDATE(pwm_id); + writel(tcon, &pwm->tcon);
+ return 0; +}
+int pwm_init(int pwm_id, int div, int invert) +{ + u32 val; + const struct s5p_timer *pwm = +#if defined(CONFIG_ARCH_NEXELL) + (struct s5p_timer *)PHY_BASEADDR_PWM; +#else + (struct s5p_timer *)samsung_get_base_timer(); +#endif + unsigned long ticks_per_period; + unsigned int offset, prescaler;
+ /* + * Timer Freq(HZ) = + * PWM_CLK / { (prescaler_value + 1) * (divider_value) } + */
+ val = readl(&pwm->tcfg0); + if (pwm_id < 2) { + prescaler = PRESCALER_0; + val &= ~0xff; + val |= (prescaler & 0xff); + } else { + prescaler = PRESCALER_1; + val &= ~(0xff << 8); + val |= (prescaler & 0xff) << 8; + } + writel(val, &pwm->tcfg0); + val = readl(&pwm->tcfg1); + val &= ~(0xf << MUX_DIV_SHIFT(pwm_id)); + val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id); + writel(val, &pwm->tcfg1);
+ if (pwm_id == 4) { + /* + * TODO(sjg): Use this as a countdown timer for now. We count + * down from the maximum value to 0, then reset. + */ + ticks_per_period = -1UL; + } else { + const unsigned long pwm_hz = 1000; +#if defined(CONFIG_ARCH_NEXELL) + struct clk *clk = clk_get(CORECLK_NAME_PCLK); + unsigned long timer_rate_hz = clk_get_rate(clk) / +#else + unsigned long timer_rate_hz = get_pwm_clk() / +#endif + ((prescaler + 1) * (1 << div));
+ ticks_per_period = timer_rate_hz / pwm_hz; + }
+ /* set count value */ + offset = pwm_id * 3;
+ writel(ticks_per_period, &pwm->tcntb0 + offset);
+ val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id)); + if (invert && pwm_id < 4) + val |= TCON_INVERTER(pwm_id); + writel(val, &pwm->tcon);
+ nx_gpio_set_pad_function(pwm_dev[pwm_id].grp, pwm_dev[pwm_id].bit, + pwm_dev[pwm_id].pwm_fn); + pwm_enable(pwm_id);
+ return 0; +} diff --git a/drivers/pwm/pwm-nexell.h b/drivers/pwm/pwm-nexell.h new file mode 100644 index 0000000..92dc707 --- /dev/null +++ b/drivers/pwm/pwm-nexell.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+
- Copyright (C) 2009 Samsung Electronics
- Kyungmin Park kyungmin.park@samsung.com
- Minkyu Kang mk7.kang@samsung.com
- */
+#ifndef __ASM_ARM_ARCH_PWM_H_ +#define __ASM_ARM_ARCH_PWM_H_
+#define PRESCALER_0 (8 - 1) /* prescaler of timer 0, 1 */ +#define PRESCALER_1 (16 - 1) /* prescaler of timer 2, 3, 4 */
+/* Divider MUX */ +#define MUX_DIV_1 0 /* 1/1 period */ +#define MUX_DIV_2 1 /* 1/2 period */ +#define MUX_DIV_4 2 /* 1/4 period */ +#define MUX_DIV_8 3 /* 1/8 period */ +#define MUX_DIV_16 4 /* 1/16 period */
+#define MUX_DIV_SHIFT(x) ((x) * 4)
+#define TCON_OFFSET(x) (((x) + 1) * (!!x) << 2)
+#define TCON_START(x) (1 << TCON_OFFSET(x)) +#define TCON_UPDATE(x) (1 << (TCON_OFFSET(x) + 1)) +#define TCON_INVERTER(x) (1 << (TCON_OFFSET(x) + 2)) +#define TCON_AUTO_RELOAD(x) (1 << (TCON_OFFSET(x) + 3)) +#define TCON4_AUTO_RELOAD (1 << 22)
+#ifndef __ASSEMBLY__ +struct s5p_timer { + unsigned int tcfg0; + unsigned int tcfg1; + unsigned int tcon; + unsigned int tcntb0; + unsigned int tcmpb0; + unsigned int tcnto0; + unsigned int tcntb1; + unsigned int tcmpb1; + unsigned int tcnto1; + unsigned int tcntb2; + unsigned int tcmpb2; + unsigned int tcnto2; + unsigned int tcntb3; + unsigned int res1; + unsigned int tcnto3; + unsigned int tcntb4; + unsigned int tcnto4; + unsigned int tintcstat; +}; +#endif /* __ASSEMBLY__ */
+#endif
bye, Heiko

Low level functions for DisplayTop (Display Topology).
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
drivers/video/nexell/soc/s5pxx18_soc_disptop.c | 185 ++++++++++ drivers/video/nexell/soc/s5pxx18_soc_disptop.h | 385 +++++++++++++++++++++ drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.c | 309 +++++++++++++++++ drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.h | 59 ++++ drivers/video/nexell/soc/s5pxx18_soc_disptype.h | 23 ++ 5 files changed, 961 insertions(+) create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptop.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptop.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_disptype.h
diff --git a/drivers/video/nexell/soc/s5pxx18_soc_disptop.c b/drivers/video/nexell/soc/s5pxx18_soc_disptop.c new file mode 100644 index 0000000..626e53a --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_disptop.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <linux/types.h> +#include <linux/io.h> + +#include "s5pxx18_soc_disptop.h" + +static struct { + struct nx_disp_top_register_set *pregister; +} __g_module_variables = { NULL, }; + +int nx_disp_top_initialize(void) +{ + static int binit; + u32 i; + + if (binit == 0) { + for (i = 0; i < NUMBER_OF_DISPTOP_MODULE; i++) + __g_module_variables.pregister = NULL; + binit = 1; + } + return 1; +} + +u32 nx_disp_top_get_number_of_module(void) +{ + return NUMBER_OF_DISPTOP_MODULE; +} + +u32 nx_disp_top_get_physical_address(void) +{ + static const u32 physical_addr[] = PHY_BASEADDR_DISPTOP_LIST; + + return (u32)(physical_addr[0] + PHY_BASEADDR_DISPLAYTOP_MODULE_OFFSET); +} + +u32 nx_disp_top_get_size_of_register_set(void) +{ + return sizeof(struct nx_disp_top_register_set); +} + +void nx_disp_top_set_base_address(void *base_address) +{ + __g_module_variables.pregister = + (struct nx_disp_top_register_set *)base_address; +} + +void *nx_disp_top_get_base_address(void) +{ + return (void *)__g_module_variables.pregister; +} + +void nx_disp_top_set_resconvmux(int benb, u32 sel) +{ + register struct nx_disp_top_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables.pregister; + regvalue = (benb << 31) | (sel << 0); + writel((u32)regvalue, &pregister->resconv_mux_ctrl); +} + +void nx_disp_top_set_hdmimux(int benb, u32 sel) +{ + register struct nx_disp_top_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables.pregister; + regvalue = (benb << 31) | (sel << 0); + writel((u32)regvalue, &pregister->interconv_mux_ctrl); +} + +void nx_disp_top_set_mipimux(int benb, u32 sel) +{ + register struct nx_disp_top_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables.pregister; + regvalue = (benb << 31) | (sel << 0); + writel((u32)regvalue, &pregister->mipi_mux_ctrl); +} + +void nx_disp_top_set_lvdsmux(int benb, u32 sel) +{ + register struct nx_disp_top_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables.pregister; + regvalue = (benb << 31) | (sel << 0); + writel((u32)regvalue, &pregister->lvds_mux_ctrl); +} + +void nx_disp_top_set_primary_mux(u32 sel) +{ + register struct nx_disp_top_register_set *pregister; + + pregister = __g_module_variables.pregister; + writel((u32)sel, &pregister->tftmpu_mux); +} + +void nx_disp_top_hdmi_set_vsync_start(u32 sel) +{ + register struct nx_disp_top_register_set *pregister; + + pregister = __g_module_variables.pregister; + writel((u32)sel, &pregister->hdmisyncctrl0); +} + +void nx_disp_top_hdmi_set_vsync_hsstart_end(u32 start, u32 end) +{ + register struct nx_disp_top_register_set *pregister; + + pregister = __g_module_variables.pregister; + writel((u32)(end << 16) | (start << 0), &pregister->hdmisyncctrl3); +} + +void nx_disp_top_hdmi_set_hactive_start(u32 sel) +{ + register struct nx_disp_top_register_set *pregister; + + pregister = __g_module_variables.pregister; + writel((u32)sel, &pregister->hdmisyncctrl1); +} + +void nx_disp_top_hdmi_set_hactive_end(u32 sel) +{ + register struct nx_disp_top_register_set *pregister; + + pregister = __g_module_variables.pregister; + writel((u32)sel, &pregister->hdmisyncctrl2); +} + +void nx_disp_top_set_hdmifield(u32 enable, u32 init_val, u32 vsynctoggle, + u32 hsynctoggle, u32 vsyncclr, u32 hsyncclr, + u32 field_use, u32 muxsel) +{ + register struct nx_disp_top_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables.pregister; + regvalue = ((enable & 0x01) << 0) | ((init_val & 0x01) << 1) | + ((vsynctoggle & 0x3fff) << 2) | + ((hsynctoggle & 0x3fff) << 17); + writel(regvalue, &pregister->hdmifieldctrl); + regvalue = ((field_use & 0x01) << 31) | ((muxsel & 0x01) << 30) | + ((hsyncclr) << 15) | ((vsyncclr) << 0); + writel(regvalue, &pregister->greg0); +} + +void nx_disp_top_set_padclock(u32 mux_index, u32 padclk_cfg) +{ + register struct nx_disp_top_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables.pregister; + regvalue = readl(&pregister->greg1); + if (padmux_secondary_mlc == mux_index) { + regvalue = regvalue & (~(0x7 << 3)); + regvalue = regvalue | (padclk_cfg << 3); + } else if (padmux_resolution_conv == mux_index) { + regvalue = regvalue & (~(0x7 << 6)); + regvalue = regvalue | (padclk_cfg << 6); + } else { + regvalue = regvalue & (~(0x7 << 0)); + regvalue = regvalue | (padclk_cfg << 0); + } + writel(regvalue, &pregister->greg1); +} + +void nx_disp_top_set_lcdif_enb(int enb) +{ + register struct nx_disp_top_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables.pregister; + regvalue = readl(&pregister->greg1); + regvalue = regvalue & (~(0x1 << 9)); + regvalue = regvalue | ((enb & 0x1) << 9); + writel(regvalue, &pregister->greg1); +} diff --git a/drivers/video/nexell/soc/s5pxx18_soc_disptop.h b/drivers/video/nexell/soc/s5pxx18_soc_disptop.h new file mode 100644 index 0000000..c7bf504 --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_disptop.h @@ -0,0 +1,385 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _S5PXX18_SOC_DISPTOP_H_ +#define _S5PXX18_SOC_DISPTOP_H_ + +#include "s5pxx18_soc_disptype.h" + +#define NUMBER_OF_DISPTOP_MODULE 1 +#define PHY_BASEADDR_DISPLAYTOP_MODULE 0xC0100000 +#define PHY_BASEADDR_DISPTOP_LIST \ + { PHY_BASEADDR_DISPLAYTOP_MODULE } + +#define HDMI_ADDR_OFFSET \ + (((PHY_BASEADDR_DISPLAYTOP_MODULE / 0x00100000) % 2) ? 0x100000 \ + : 0x000000) +#define OTHER_ADDR_OFFSET \ + (((PHY_BASEADDR_DISPLAYTOP_MODULE / 0x00100000) % 2) ? 0x000000 \ + : 0x100000) +#define PHY_BASEADDR_DISPLAYTOP_MODULE_OFFSET (OTHER_ADDR_OFFSET + 0x001000) +#define PHY_BASEADDR_DUALDISPLAY_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x002000) +#define PHY_BASEADDR_RESCONV_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x003000) +#define PHY_BASEADDR_LCDINTERFACE_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x004000) +#define PHY_BASEADDR_HDMI_MODULE (PHY_BASEADDR_DISPLAYTOP_MODULE + 0x000000) +#define PHY_BASEADDR_LVDS_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x00a000) + +#define NUMBER_OF_DUALDISPLAY_MODULE 1 +#define INTNUM_OF_DUALDISPLAY_MODULE_PRIMIRQ \ + INTNUM_OF_DISPLAYTOP_MODULE_DUALDISPLAY_PRIMIRQ +#define INTNUM_OF_DUALDISPLAY_MODULE_SECONDIRQ \ + INTNUM_OF_DISPLAYTOP_MODULE_DUALDISPLAY_SECONDIRQ +#define RESETINDEX_OF_DUALDISPLAY_MODULE_I_NRST \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_DUALDISPLAY_NRST +#define PADINDEX_OF_DUALDISPLAY_O_NCS \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PADPRIMVCLK +#define PADINDEX_OF_DUALDISPLAY_O_NRD \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADN_HSYNC +#define PADINDEX_OF_DUALDISPLAY_O_RS \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADN_VSYNC +#define PADINDEX_OF_DUALDISPLAY_O_NWR \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADDE +#define PADINDEX_OF_DUALDISPLAY_PADPRIMVCLK \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PADPRIMVCLK +#define PADINDEX_OF_DUALDISPLAY_O_PRIM_PADN_HSYNC \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADN_HSYNC +#define PADINDEX_OF_DUALDISPLAY_O_PRIM_PADN_VSYNC \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADN_VSYNC +#define PADINDEX_OF_DUALDISPLAY_O_PRIM_PADDE \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADDE +#define PADINDEX_OF_DUALDISPLAY_PRIM_0_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_0_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_1_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_1_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_2_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_2_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_3_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_3_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_4_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_4_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_5_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_5_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_6_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_6_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_7_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_7_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_8_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_8_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_9_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_9_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_10_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_10_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_11_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_11_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_12_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_12_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_13_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_13_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_14_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_14_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_15_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_15_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_16_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_16_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_17_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_17_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_18_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_18_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_19_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_19_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_20_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_20_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_21_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_21_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_22_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_22_ +#define PADINDEX_OF_DUALDISPLAY_PRIM_23_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_23_ +#define PADINDEX_OF_DUALDISPLAY_PADSECONDVCLK \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PADPRIMVCLK +#define PADINDEX_OF_DUALDISPLAY_O_SECOND_PADN_HSYNC \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADN_HSYNC +#define PADINDEX_OF_DUALDISPLAY_O_SECOND_PADN_VSYNC \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADN_VSYNC +#define PADINDEX_OF_DUALDISPLAY_O_SECOND_PADDE \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADDE +#define PADINDEX_OF_DUALDISPLAY_SECOND_0_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_0_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_1_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_1_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_2_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_2_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_3_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_3_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_4_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_4_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_5_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_5_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_6_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_6_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_7_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_7_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_8_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_8_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_9_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_9_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_10_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_10_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_11_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_11_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_12_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_12_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_13_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_13_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_14_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_14_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_15_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_15_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_16_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_16_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_17_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_17_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_18_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_18_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_19_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_19_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_20_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_20_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_21_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_21_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_22_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_22_ +#define PADINDEX_OF_DUALDISPLAY_SECOND_23_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_23_ + +#define NUMBER_OF_RESCONV_MODULE 1 +#define INTNUM_OF_RESCONV_MODULE INTNUM_OF_DISPLAYTOP_MODULE_RESCONV_IRQ +#define RESETINDEX_OF_RESCONV_MODULE_I_NRST \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_RESCONV_NRST +#define RESETINDEX_OF_RESCONV_MODULE RESETINDEX_OF_RESCONV_MODULE_I_NRST +#define NUMBER_OF_LCDINTERFACE_MODULE 1 +#define RESETINDEX_OF_LCDINTERFACE_MODULE_I_NRST \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_LCDIF_NRST +#define PADINDEX_OF_LCDINTERFACE_O_VCLK \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PADPRIMVCLK +#define PADINDEX_OF_LCDINTERFACE_O_NHSYNC \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADN_HSYNC +#define PADINDEX_OF_LCDINTERFACE_O_NVSYNC \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADN_VSYNC +#define PADINDEX_OF_LCDINTERFACE_O_DE \ + PADINDEX_OF_DISPLAYTOP_O_DUAL_DISPLAY_PRIM_PADDE +#define PADINDEX_OF_LCDINTERFACE_RGB24_0_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_0_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_1_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_1_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_2_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_2_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_3_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_3_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_4_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_4_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_5_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_5_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_6_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_6_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_7_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_7_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_8_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_8_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_9_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_9_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_10_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_10_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_11_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_11_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_12_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_12_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_13_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_13_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_14_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_14_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_15_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_15_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_16_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_16_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_17_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_17_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_18_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_18_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_19_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_19_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_20_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_20_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_21_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_21_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_22_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_22_ +#define PADINDEX_OF_LCDINTERFACE_RGB24_23_ \ + PADINDEX_OF_DISPLAYTOP_DUAL_DISPLAY_PRIM_23_ + +#define NUMBER_OF_HDMI_MODULE 1 +#define INTNUM_OF_HDMI_MODULE INTNUM_OF_DISPLAYTOP_MODULE_HDMI_IRQ +#define RESETINDEX_OF_HDMI_MODULE_I_NRST \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_HDMI_NRST +#define RESETINDEX_OF_HDMI_MODULE_I_NRST_VIDEO \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_HDMI_VIDEO_NRST +#define RESETINDEX_OF_HDMI_MODULE_I_NRST_SPDIF \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_HDMI_SPDIF_NRST +#define RESETINDEX_OF_HDMI_MODULE_I_NRST_TMDS \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_HDMI_TMDS_NRST +#define RESETINDEX_OF_HDMI_MODULE_I_NRST_PHY \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_HDMI_PHY_NRST +#define PADINDEX_OF_HDMI_I_PHY_CLKI PADINDEX_OF_DISPLAYTOP_I_HDMI_CLKI +#define PADINDEX_OF_HDMI_O_PHY_CLKO PADINDEX_OF_DISPLAYTOP_O_HDMI_CLKO +#define PADINDEX_OF_HDMI_IO_PHY_REXT PADINDEX_OF_DISPLAYTOP_IO_HDMI_REXT +#define PADINDEX_OF_HDMI_O_PHY_TX0P PADINDEX_OF_DISPLAYTOP_O_HDMI_TX0P +#define PADINDEX_OF_HDMI_O_PHY_TX0N PADINDEX_OF_DISPLAYTOP_O_HDMI_TX0N +#define PADINDEX_OF_HDMI_O_PHY_TX1P PADINDEX_OF_DISPLAYTOP_O_HDMI_TX1P +#define PADINDEX_OF_HDMI_O_PHY_TX1N PADINDEX_OF_DISPLAYTOP_O_HDMI_TX1N +#define PADINDEX_OF_HDMI_O_PHY_TX2P PADINDEX_OF_DISPLAYTOP_O_HDMI_TX2P +#define PADINDEX_OF_HDMI_O_PHY_TX2N PADINDEX_OF_DISPLAYTOP_O_HDMI_TX2N +#define PADINDEX_OF_HDMI_O_PHY_TXCP PADINDEX_OF_DISPLAYTOP_O_HDMI_TXCP +#define PADINDEX_OF_HDMI_O_PHY_TXCN PADINDEX_OF_DISPLAYTOP_O_HDMI_TXCN +#define PADINDEX_OF_HDMI_I_HOTPLUG PADINDEX_OF_DISPLAYTOP_I_HDMI_HOTPLUG_5V +#define PADINDEX_OF_HDMI_IO_PAD_CEC PADINDEX_OF_DISPLAYTOP_IO_HDMI_CEC +#define NUMBER_OF_LVDS_MODULE 1 + +#define RESETINDEX_OF_LVDS_MODULE_I_RESETN \ + RESETINDEX_OF_DISPLAYTOP_MODULE_I_LVDS_NRST +#define RESETINDEX_OF_LVDS_MODULE RESETINDEX_OF_LVDS_MODULE_I_RESETN + +#define PADINDEX_OF_LVDS_TAP PADINDEX_OF_DISPLAYTOP_LVDS_TXP_A +#define PADINDEX_OF_LVDS_TAN PADINDEX_OF_DISPLAYTOP_LVDS_TXN_A +#define PADINDEX_OF_LVDS_TBP PADINDEX_OF_DISPLAYTOP_LVDS_TXP_B +#define PADINDEX_OF_LVDS_TBN PADINDEX_OF_DISPLAYTOP_LVDS_TXN_B +#define PADINDEX_OF_LVDS_TCP PADINDEX_OF_DISPLAYTOP_LVDS_TXP_C +#define PADINDEX_OF_LVDS_TCN PADINDEX_OF_DISPLAYTOP_LVDS_TXN_C +#define PADINDEX_OF_LVDS_TDP PADINDEX_OF_DISPLAYTOP_LVDS_TXP_D +#define PADINDEX_OF_LVDS_TDN PADINDEX_OF_DISPLAYTOP_LVDS_TXN_D +#define PADINDEX_OF_LVDS_TCLKP PADINDEX_OF_DISPLAYTOP_LVDS_TXP_CLK +#define PADINDEX_OF_LVDS_TCLKN PADINDEX_OF_DISPLAYTOP_LVDS_TXN_CLK +#define PADINDEX_OF_LVDS_ROUT PADINDEX_OF_DISPLAYTOP_LVDS_ROUT +#define PADINDEX_OF_LVDS_TEP PADINDEX_OF_DISPLAYTOP_LVDS_TXN_E +#define PADINDEX_OF_LVDS_TEN PADINDEX_OF_DISPLAYTOP_LVDS_TXN_E +#define NUMBER_OF_DISPTOP_CLKGEN_MODULE 5 + +enum disptop_clkgen_module_index { + res_conv_clkgen = 0, + lcdif_clkgen = 1, + to_mipi_clkgen = 2, + to_lvds_clkgen = 3, + hdmi_clkgen = 4, +}; + +enum disptop_res_conv_iclk_cclk { + res_conv_iclk = 0, + res_conv_cclk = 1, +}; + +enum disptop_res_conv_oclk { + res_conv_oclk = 1, +}; + +enum disptop_lcdif_clk { + lcdif_pixel_clkx_n = 0, + lcdif_pixel_clk = 1, +}; + +#define HDMI_SPDIF_CLKGEN 2 +#define HDMI_SPDIF_CLKOUT 0 +#define HDMI_I_VCLK_CLKOUT 0 +#define PHY_BASEADDR_DISPTOP_CLKGEN0_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x006000) +#define PHY_BASEADDR_DISPTOP_CLKGEN1_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x007000) +#define PHY_BASEADDR_DISPTOP_CLKGEN2_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x005000) +#define PHY_BASEADDR_DISPTOP_CLKGEN3_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x008000) +#define PHY_BASEADDR_DISPTOP_CLKGEN4_MODULE \ + (PHY_BASEADDR_DISPLAYTOP_MODULE + OTHER_ADDR_OFFSET + 0x009000) + +struct nx_disp_top_register_set { + u32 resconv_mux_ctrl; + u32 interconv_mux_ctrl; + u32 mipi_mux_ctrl; + u32 lvds_mux_ctrl; + u32 hdmifixctrl0; + u32 hdmisyncctrl0; + u32 hdmisyncctrl1; + u32 hdmisyncctrl2; + u32 hdmisyncctrl3; + u32 tftmpu_mux; + u32 hdmifieldctrl; + u32 greg0; + u32 greg1; + u32 greg2; + u32 greg3; + u32 greg4; + u32 greg5; +}; + +int nx_disp_top_initialize(void); +u32 nx_disp_top_get_number_of_module(void); + +u32 nx_disp_top_get_physical_address(void); +u32 nx_disp_top_get_size_of_register_set(void); +void nx_disp_top_set_base_address(void *base_address); +void *nx_disp_top_get_base_address(void); +int nx_disp_top_open_module(void); +int nx_disp_top_close_module(void); +int nx_disp_top_check_busy(void); + +enum mux_index { + primary_mlc = 0, + secondary_mlc = 1, + resolution_conv = 2, +}; + +enum prim_pad_mux_index { + padmux_primary_mlc = 0, + padmux_primary_mpu = 1, + padmux_secondary_mlc = 2, + padmux_resolution_conv = 3, +}; + +void nx_disp_top_set_resconvmux(int benb, u32 sel); +void nx_disp_top_set_hdmimux(int benb, u32 sel); +void nx_disp_top_set_mipimux(int benb, u32 sel); +void nx_disp_top_set_lvdsmux(int benb, u32 sel); +void nx_disp_top_set_primary_mux(u32 sel); +void nx_disp_top_hdmi_set_vsync_start(u32 sel); +void nx_disp_top_hdmi_set_vsync_hsstart_end(u32 start, u32 end); +void nx_disp_top_hdmi_set_hactive_start(u32 sel); +void nx_disp_top_hdmi_set_hactive_end(u32 sel); + +void nx_disp_top_set_hdmifield(u32 enable, u32 init_val, u32 vsynctoggle, + u32 hsynctoggle, u32 vsyncclr, u32 hsyncclr, + u32 field_use, u32 muxsel); + +enum padclk_config { + padclk_clk = 0, + padclk_inv_clk = 1, + padclk_reserved_clk = 2, + padclk_reserved_inv_clk = 3, + padclk_clk_div2_0 = 4, + padclk_clk_div2_90 = 5, + padclk_clk_div2_180 = 6, + padclk_clk_div2_270 = 7, +}; + +void nx_disp_top_set_padclock(u32 mux_index, u32 padclk_cfg); +void nx_disp_top_set_lcdif_enb(int enb); +void nx_disp_top_set_hdmifield(u32 enable, u32 init_val, u32 vsynctoggle, + u32 hsynctoggle, u32 vsyncclr, u32 hsyncclr, + u32 field_use, u32 muxsel); + +#endif diff --git a/drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.c b/drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.c new file mode 100644 index 0000000..02361ba --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <linux/types.h> +#include <linux/io.h> + +#include "s5pxx18_soc_disptop_clk.h" +#include "s5pxx18_soc_disptop.h" + +static struct { + struct nx_disptop_clkgen_register_set *__g_pregister; +} __g_module_variables[NUMBER_OF_DISPTOP_CLKGEN_MODULE] = { + { NULL,}, +}; + +int nx_disp_top_clkgen_initialize(void) +{ + static int binit; + u32 i; + + if (binit == 0) { + for (i = 0; i < NUMBER_OF_DISPTOP_CLKGEN_MODULE; i++) + __g_module_variables[i].__g_pregister = NULL; + binit = 1; + } + return 1; +} + +u32 nx_disp_top_clkgen_get_number_of_module(void) +{ + return NUMBER_OF_DISPTOP_CLKGEN_MODULE; +} + +u32 nx_disp_top_clkgen_get_physical_address(u32 module_index) +{ + static const u32 physical_addr[] = + PHY_BASEADDR_DISPTOP_CLKGEN_LIST; + + return (u32)physical_addr[module_index]; +} + +u32 nx_disp_top_clkgen_get_size_of_register_set(void) +{ + return sizeof(struct nx_disptop_clkgen_register_set); +} + +void nx_disp_top_clkgen_set_base_address(u32 module_index, void *base_address) +{ + __g_module_variables[module_index].__g_pregister = + (struct nx_disptop_clkgen_register_set *)base_address; +} + +void *nx_disp_top_clkgen_get_base_address(u32 module_index) +{ + return (void *)__g_module_variables[module_index].__g_pregister; +} + +void nx_disp_top_clkgen_set_clock_bclk_mode(u32 module_index, + enum nx_bclkmode mode) +{ + register struct nx_disptop_clkgen_register_set *pregister; + register u32 regvalue; + u32 clkmode = 0; + + pregister = __g_module_variables[module_index].__g_pregister; + switch (mode) { + case nx_bclkmode_disable: + clkmode = 0; + case nx_bclkmode_dynamic: + clkmode = 2; + break; + case nx_bclkmode_always: + clkmode = 3; + break; + default: + break; + } + + regvalue = pregister->clkenb; + regvalue &= ~3ul; + regvalue |= (clkmode & 0x03); + + writel(regvalue, &pregister->clkenb); +} + +enum nx_bclkmode nx_disp_top_clkgen_get_clock_bclk_mode(u32 module_index) +{ + register struct nx_disptop_clkgen_register_set *pregister; + u32 mode = 0; + + pregister = __g_module_variables[module_index].__g_pregister; + mode = (pregister->clkenb & 3ul); + + switch (mode) { + case 0: + return nx_bclkmode_disable; + case 2: + return nx_bclkmode_dynamic; + case 3: + return nx_bclkmode_always; + default: + break; + } + return nx_bclkmode_disable; +} + +void nx_disp_top_clkgen_set_clock_pclk_mode(u32 module_index, + enum nx_pclkmode mode) +{ + register struct nx_disptop_clkgen_register_set *pregister; + register u32 regvalue; + const u32 pclkmode_pos = 3; + u32 clkmode = 0; + + pregister = __g_module_variables[module_index].__g_pregister; + switch (mode) { + case nx_pclkmode_dynamic: + clkmode = 0; + break; + case nx_pclkmode_always: + clkmode = 1; + break; + default: + break; + } + + regvalue = pregister->clkenb; + regvalue &= ~(1ul << pclkmode_pos); + regvalue |= (clkmode & 0x01) << pclkmode_pos; + + writel(regvalue, &pregister->clkenb); +} + +enum nx_pclkmode nx_disp_top_clkgen_get_clock_pclk_mode(u32 module_index) +{ + register struct nx_disptop_clkgen_register_set *pregister; + const u32 pclkmode_pos = 3; + + pregister = __g_module_variables[module_index].__g_pregister; + + if (pregister->clkenb & (1ul << pclkmode_pos)) + return nx_pclkmode_always; + + return nx_pclkmode_dynamic; +} + +void nx_disp_top_clkgen_set_clock_source(u32 module_index, u32 index, + u32 clk_src) +{ + register struct nx_disptop_clkgen_register_set *pregister; + register u32 read_value; + + const u32 clksrcsel_pos = 2; + const u32 clksrcsel_mask = 0x07 << clksrcsel_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + read_value = pregister->CLKGEN[index << 1]; + read_value &= ~clksrcsel_mask; + read_value |= clk_src << clksrcsel_pos; + + writel(read_value, &pregister->CLKGEN[index << 1]); +} + +u32 nx_disp_top_clkgen_get_clock_source(u32 module_index, u32 index) +{ + register struct nx_disptop_clkgen_register_set *pregister; + const u32 clksrcsel_pos = 2; + const u32 clksrcsel_mask = 0x07 << clksrcsel_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + return (pregister->CLKGEN[index << 1] & + clksrcsel_mask) >> clksrcsel_pos; +} + +void nx_disp_top_clkgen_set_clock_divisor(u32 module_index, u32 index, + u32 divisor) +{ + register struct nx_disptop_clkgen_register_set *pregister; + const u32 clkdiv_pos = 5; + const u32 clkdiv_mask = 0xff << clkdiv_pos; + register u32 read_value; + + pregister = __g_module_variables[module_index].__g_pregister; + + read_value = pregister->CLKGEN[index << 1]; + read_value &= ~clkdiv_mask; + read_value |= (divisor - 1) << clkdiv_pos; + writel(read_value, &pregister->CLKGEN[index << 1]); +} + +u32 nx_disp_top_clkgen_get_clock_divisor(u32 module_index, u32 index) +{ + register struct nx_disptop_clkgen_register_set *pregister; + const u32 clkdiv_pos = 5; + const u32 clkdiv_mask = 0xff << clkdiv_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + return ((pregister->CLKGEN[index << 1] & + clkdiv_mask) >> clkdiv_pos) + 1; +} + +void nx_disp_top_clkgen_set_clock_divisor_enable(u32 module_index, int enable) +{ + register struct nx_disptop_clkgen_register_set *pregister; + register u32 read_value; + const u32 clkgenenb_pos = 2; + const u32 clkgenenb_mask = 1ul << clkgenenb_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + read_value = pregister->clkenb; + read_value &= ~clkgenenb_mask; + read_value |= (u32)enable << clkgenenb_pos; + + writel(read_value, &pregister->clkenb); +} + +int nx_disp_top_clkgen_get_clock_divisor_enable(u32 module_index) +{ + register struct nx_disptop_clkgen_register_set *pregister; + const u32 clkgenenb_pos = 2; + const u32 clkgenenb_mask = 1ul << clkgenenb_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + return (int)((pregister->clkenb & + clkgenenb_mask) >> clkgenenb_pos); +} + +void nx_disp_top_clkgen_set_clock_out_inv(u32 module_index, u32 index, + int out_clk_inv) +{ + register struct nx_disptop_clkgen_register_set *pregister; + register u32 read_value; + const u32 outclkinv_pos = 1; + const u32 outclkinv_mask = 1ul << outclkinv_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + read_value = pregister->CLKGEN[index << 1]; + read_value &= ~outclkinv_mask; + read_value |= out_clk_inv << outclkinv_pos; + + writel(read_value, &pregister->CLKGEN[index << 1]); +} + +int nx_disp_top_clkgen_get_clock_out_inv(u32 module_index, u32 index) +{ + register struct nx_disptop_clkgen_register_set *pregister; + const u32 outclkinv_pos = 1; + const u32 outclkinv_mask = 1ul << outclkinv_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + return (int)((pregister->CLKGEN[index << 1] & + outclkinv_mask) >> outclkinv_pos); +} + +int nx_disp_top_clkgen_set_input_inv(u32 module_index, + u32 index, int in_clk_inv) +{ + register struct nx_disptop_clkgen_register_set *pregister; + register u32 read_value; + const u32 inclkinv_pos = 4 + index; + const u32 inclkinv_mask = 1ul << inclkinv_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + read_value = pregister->clkenb; + read_value &= ~inclkinv_mask; + read_value |= in_clk_inv << inclkinv_pos; + + writel(read_value, &pregister->clkenb); + return true; +} + +int nx_disp_top_clkgen_get_input_inv(u32 module_index, u32 index) +{ + register struct nx_disptop_clkgen_register_set *pregister; + const u32 inclkinv_pos = 4 + index; + const u32 inclkinv_mask = 1ul << inclkinv_pos; + + pregister = __g_module_variables[module_index].__g_pregister; + + return (int)((pregister->clkenb & + inclkinv_mask) >> inclkinv_pos); +} + +void nx_disp_top_clkgen_set_clock_out_select(u32 module_index, u32 index, + int bbypass) +{ + register struct nx_disptop_clkgen_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].__g_pregister; + + read_value = pregister->CLKGEN[index << 1]; + read_value = read_value & (~0x01); + read_value = read_value | bbypass; + + writel(read_value, &pregister->CLKGEN[index << 1]); +} diff --git a/drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.h b/drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.h new file mode 100644 index 0000000..d55fef7 --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_disptop_clk.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _S5PXX18_SOC_DISPTOP_CLK_H_ +#define _S5PXX18_SOC_DISPTOP_CLK_H_ + +#include "s5pxx18_soc_disptype.h" + +#define PHY_BASEADDR_DISPTOP_CLKGEN_LIST \ + { PHY_BASEADDR_DISPTOP_CLKGEN0_MODULE, \ + PHY_BASEADDR_DISPTOP_CLKGEN1_MODULE, \ + PHY_BASEADDR_DISPTOP_CLKGEN2_MODULE, \ + PHY_BASEADDR_DISPTOP_CLKGEN3_MODULE, \ + PHY_BASEADDR_DISPTOP_CLKGEN4_MODULE, \ + } + +struct nx_disptop_clkgen_register_set { + u32 clkenb; + u32 CLKGEN[4]; +}; + +int nx_disp_top_clkgen_initialize(void); +u32 nx_disp_top_clkgen_get_number_of_module(void); +u32 nx_disp_top_clkgen_get_physical_address(u32 module_index); +u32 nx_disp_top_clkgen_get_size_of_register_set(void); +void nx_disp_top_clkgen_set_base_address(u32 module_index, + void *base_address); +void *nx_disp_top_clkgen_get_base_address(u32 module_index); +void nx_disp_top_clkgen_set_clock_pclk_mode(u32 module_index, + enum nx_pclkmode mode); +enum nx_pclkmode nx_disp_top_clkgen_get_clock_pclk_mode(u32 module_index); +void nx_disp_top_clkgen_set_clock_source(u32 module_index, u32 index, + u32 clk_src); +u32 nx_disp_top_clkgen_get_clock_source(u32 module_index, u32 index); +void nx_disp_top_clkgen_set_clock_divisor(u32 module_index, u32 index, + u32 divisor); +u32 nx_disp_top_clkgen_get_clock_divisor(u32 module_index, u32 index); +void nx_disp_top_clkgen_set_clock_divisor_enable(u32 module_index, + int enable); +int nx_disp_top_clkgen_get_clock_divisor_enable(u32 module_index); +void nx_disp_top_clkgen_set_clock_bclk_mode(u32 module_index, + enum nx_bclkmode mode); +enum nx_bclkmode nx_disp_top_clkgen_get_clock_bclk_mode(u32 module_index); + +void nx_disp_top_clkgen_set_clock_out_inv(u32 module_index, u32 index, + int out_clk_inv); +int nx_disp_top_clkgen_get_clock_out_inv(u32 module_index, u32 index); +int nx_disp_top_clkgen_set_input_inv(u32 module_index, u32 index, + int out_clk_inv); +int nx_disp_top_clkgen_get_input_inv(u32 module_index, u32 index); + +void nx_disp_top_clkgen_set_clock_out_select(u32 module_index, u32 index, + int bbypass); + +#endif diff --git a/drivers/video/nexell/soc/s5pxx18_soc_disptype.h b/drivers/video/nexell/soc/s5pxx18_soc_disptype.h new file mode 100644 index 0000000..b5df7a7 --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_disptype.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _S5PXX18_SOC_DISP_TYPE_H_ +#define _S5PXX18_SOC_DISP_TYPE_H_ + +/* clock control types */ +enum nx_pclkmode { + nx_pclkmode_dynamic = 0UL, + nx_pclkmode_always = 1UL +}; + +enum nx_bclkmode { + nx_bclkmode_disable = 0UL, + nx_bclkmode_dynamic = 2UL, + nx_bclkmode_always = 3UL +}; + +#endif

Low level functions for MLC (Multi Layer Control) and MIPI (Mobile Industry Processor Interface).
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
drivers/video/nexell/soc/s5pxx18_soc_mipi.c | 580 +++++++++ drivers/video/nexell/soc/s5pxx18_soc_mipi.h | 291 +++++ drivers/video/nexell/soc/s5pxx18_soc_mlc.c | 1861 +++++++++++++++++++++++++++ drivers/video/nexell/soc/s5pxx18_soc_mlc.h | 429 ++++++ 4 files changed, 3161 insertions(+) create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_mipi.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_mipi.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_mlc.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_mlc.h
diff --git a/drivers/video/nexell/soc/s5pxx18_soc_mipi.c b/drivers/video/nexell/soc/s5pxx18_soc_mipi.c new file mode 100644 index 0000000..1000ddb --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_mipi.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <linux/types.h> +#include <linux/io.h> + +#include "s5pxx18_soc_disptop.h" +#include "s5pxx18_soc_mipi.h" + +static struct nx_mipi_register_set *__g_pregister[NUMBER_OF_MIPI_MODULE]; + +int nx_mipi_smoke_test(u32 module_index) +{ + register struct nx_mipi_register_set *pregister; + + pregister = __g_pregister[module_index]; + + if (pregister->csis_config_ch0 != 0x000000FC) + return false; + + if (pregister->dsim_intmsk != 0xB337FFFF) + return false; + + writel(0xDEADC0DE, &pregister->csis_dphyctrl); + writel(0xFFFFFFFF, &pregister->csis_ctrl2); + writel(0xDEADC0DE, &pregister->dsim_msync); + + if (pregister->csis_dphyctrl != 0xDE80001E) + return false; + + if ((pregister->csis_ctrl2 & (~1)) != 0xEEE00010) + return false; + + if (pregister->dsim_msync != 0xDE80C0DE) + return false; + + return true; +} + +void nx_mipi_set_base_address(u32 module_index, void *base_address) +{ + __g_pregister[module_index] = + (struct nx_mipi_register_set *)base_address; +} + +void *nx_mipi_get_base_address(u32 module_index) +{ + return (void *)__g_pregister[module_index]; +} + +u32 nx_mipi_get_physical_address(u32 module_index) +{ + const u32 physical_addr[] = PHY_BASEADDR_MIPI_LIST; + + return physical_addr[module_index]; +} + +#define __nx_mipi_valid_dsi_intmask__ \ + (~((1 << 26) | (1 << 23) | (1 << 22) | (1 << 19))) + +void nx_mipi_set_interrupt_enable(u32 module_index, u32 int_num, int enable) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + + pregister = __g_pregister[module_index]; + if (int_num < 32) { + regvalue = pregister->csis_intmsk; + regvalue &= ~(1ul << int_num); + regvalue |= (u32)enable << int_num; + writel(regvalue, &pregister->csis_intmsk); + } else { + regvalue = pregister->dsim_intmsk; + regvalue &= ~(1ul << (int_num - 32)); + regvalue |= (u32)enable << (int_num - 32); + writel(regvalue, &pregister->dsim_intmsk); + } +} + +int nx_mipi_get_interrupt_enable(u32 module_index, u32 int_num) +{ + if (int_num < 32) + return (int)((__g_pregister[module_index]->csis_intmsk >> + int_num) & 0x01); + else + return (int)((__g_pregister[module_index]->dsim_intmsk >> + (int_num - 32)) & 0x01); +} + +int nx_mipi_get_interrupt_pending(u32 module_index, u32 int_num) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + int ret; + + pregister = __g_pregister[module_index]; + if (int_num < 32) { + regvalue = pregister->csis_intmsk; + regvalue &= pregister->csis_intsrc; + ret = (int)((regvalue >> int_num) & 0x01); + } else { + regvalue = pregister->dsim_intmsk; + regvalue &= pregister->dsim_intsrc; + ret = (int)((regvalue >> (int_num - 32)) & 0x01); + } + + return ret; +} + +void nx_mipi_clear_interrupt_pending(u32 module_index, u32 int_num) +{ + register struct nx_mipi_register_set *pregister; + + pregister = __g_pregister[module_index]; + if (int_num < 32) + writel(1ul << int_num, &pregister->csis_intsrc); + else + writel(1ul << (int_num - 32), &pregister->dsim_intsrc); +} + +void nx_mipi_set_interrupt_enable_all(u32 module_index, int enable) +{ + register struct nx_mipi_register_set *pregister; + + pregister = __g_pregister[module_index]; + if (enable) + writel(__nx_mipi_valid_dsi_intmask__, &pregister->dsim_intmsk); + else + writel(0, &pregister->dsim_intmsk); +} + +int nx_mipi_get_interrupt_enable_all(u32 module_index) +{ + if (__g_pregister[module_index]->csis_intmsk) + return true; + + if (__g_pregister[module_index]->dsim_intmsk) + return true; + + return false; +} + +int nx_mipi_get_interrupt_pending_all(u32 module_index) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + + pregister = __g_pregister[module_index]; + regvalue = pregister->csis_intmsk; + regvalue &= pregister->csis_intsrc; + + if (regvalue) + return true; + + regvalue = pregister->dsim_intmsk; + regvalue &= pregister->dsim_intsrc; + + if (regvalue) + return true; + + return false; +} + +void nx_mipi_clear_interrupt_pending_all(u32 module_index) +{ + register struct nx_mipi_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(__nx_mipi_valid_dsi_intmask__, &pregister->dsim_intsrc); +} + +int32_t nx_mipi_get_interrupt_pending_number(u32 module_index) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + int i; + + pregister = __g_pregister[module_index]; + regvalue = pregister->csis_intmsk; + regvalue &= pregister->csis_intsrc; + if (regvalue != 0) { + for (i = 0; i < 32; i++) { + if (regvalue & 1ul) + return i; + regvalue >>= 1; + } + } + + regvalue = pregister->dsim_intmsk; + regvalue &= pregister->dsim_intsrc; + if (regvalue != 0) { + for (i = 0; i < 32; i++) { + if (regvalue & 1ul) + return i + 32; + regvalue >>= 1; + } + } + return -1; +} + +#define writereg(regname, mask, value) \ + regvalue = pregister->(regname); \ + regvalue = (regvalue & (~(mask))) | (value); \ + writel(regvalue, &pregister->(regname)) + +void nx_mipi_dsi_get_status(u32 module_index, u32 *pulps, u32 *pstop, + u32 *pispllstable, u32 *pisinreset, + u32 *pisbackward, u32 *pishsclockready) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + + pregister = __g_pregister[module_index]; + regvalue = pregister->dsim_status; + if (pulps) { + *pulps = 0; + if (regvalue & (1 << 4)) + *pulps |= (1 << 0); + if (regvalue & (1 << 5)) + *pulps |= (1 << 1); + if (regvalue & (1 << 6)) + *pulps |= (1 << 2); + if (regvalue & (1 << 7)) + *pulps |= (1 << 3); + if (regvalue & (1 << 9)) + *pulps |= (1 << 4); + } + + if (pstop) { + *pstop = 0; + if (regvalue & (1 << 0)) + *pstop |= (1 << 0); + if (regvalue & (1 << 1)) + *pstop |= (1 << 1); + if (regvalue & (1 << 2)) + *pstop |= (1 << 2); + if (regvalue & (1 << 3)) + *pstop |= (1 << 3); + if (regvalue & (1 << 8)) + *pstop |= (1 << 4); + } + + if (pispllstable) + *pispllstable = (regvalue >> 31) & 1; + + if (pisinreset) + *pisinreset = ((regvalue >> 20) & 1) ? 0 : 1; + + if (pisbackward) + *pisbackward = (regvalue >> 16) & 1; + + if (pishsclockready) + *pishsclockready = (regvalue >> 10) & 1; +} + +void nx_mipi_dsi_software_reset(u32 module_index) +{ + register struct nx_mipi_register_set *pregister; + + pregister = __g_pregister[module_index]; + + writel(0x00010001, &pregister->dsim_swrst); + + while (0 != (readl(&pregister->dsim_status) & (1 << 20))) + ; + + writel(0x00000000, &pregister->dsim_swrst); +} + +void nx_mipi_dsi_set_clock(u32 module_index, int enable_txhsclock, + int use_external_clock, int enable_byte_clock, + int enable_escclock_clock_lane, + int enable_escclock_data_lane0, + int enable_escclock_data_lane1, + int enable_escclock_data_lane2, + int enable_escclock_data_lane3, + int enable_escprescaler, u32 escprescalervalue) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + + pregister = __g_pregister[module_index]; + regvalue = 0; + regvalue |= (enable_txhsclock << 31); + regvalue |= (use_external_clock << 27); + regvalue |= (enable_byte_clock << 24); + regvalue |= (enable_escclock_clock_lane << 19); + regvalue |= (enable_escclock_data_lane0 << 20); + regvalue |= (enable_escclock_data_lane1 << 21); + regvalue |= (enable_escclock_data_lane2 << 22); + regvalue |= (enable_escclock_data_lane3 << 23); + regvalue |= (enable_escprescaler << 28); + regvalue |= escprescalervalue; + + writel(regvalue, &pregister->dsim_clkctrl); +} + +void nx_mipi_dsi_set_timeout(u32 module_index, u32 bta_tout, u32 lpdrtout) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + + pregister = __g_pregister[module_index]; + regvalue = 0; + regvalue |= (bta_tout << 16); + regvalue |= (lpdrtout << 0); + + writel(regvalue, &pregister->dsim_timeout); +} + +void nx_mipi_dsi_set_config_video_mode(u32 module_index, + int enable_auto_flush_main_display_fifo, + int enable_auto_vertical_count, + int enable_burst, + enum nx_mipi_dsi_syncmode sync_mode, + int enable_eo_tpacket, + int enable_hsync_end_packet, + int enable_hfp, int enable_hbp, + int enable_hsa, + u32 number_of_virtual_channel, + enum nx_mipi_dsi_format format, + u32 number_of_words_in_hfp, + u32 number_of_words_in_hbp, + u32 number_of_words_in_hsync, + u32 number_of_lines_in_vfp, + u32 number_of_lines_in_vbp, + u32 number_of_lines_in_vsync, + u32 number_of_lines_in_command_allow) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + u32 newvalue; + + pregister = __g_pregister[module_index]; + newvalue = (1 << 25); + newvalue |= ((1 - enable_auto_flush_main_display_fifo) << 29); + newvalue |= (enable_auto_vertical_count << 24); + newvalue |= (enable_burst << 26); + newvalue |= (sync_mode << 27); + newvalue |= ((1 - enable_eo_tpacket) << 28); + newvalue |= (enable_hsync_end_packet << 23); + newvalue |= ((1 - enable_hfp) << 22); + newvalue |= ((1 - enable_hbp) << 21); + newvalue |= ((1 - enable_hsa) << 20); + newvalue |= (number_of_virtual_channel << 18); + newvalue |= (format << 12); + + writereg(dsim_config, 0xFFFFFF00, newvalue); + + newvalue = (number_of_lines_in_command_allow << 28); + newvalue |= (number_of_lines_in_vfp << 16); + newvalue |= (number_of_lines_in_vbp << 0); + + writel(newvalue, &pregister->dsim_mvporch); + + newvalue = (number_of_words_in_hfp << 16); + newvalue |= (number_of_words_in_hbp << 0); + + writel(newvalue, &pregister->dsim_mhporch); + + newvalue = (number_of_words_in_hsync << 0); + newvalue |= (number_of_lines_in_vsync << 22); + + writel(newvalue, &pregister->dsim_msync); +} + +void nx_mipi_dsi_set_config_command_mode(u32 module_index, + int + enable_auto_flush_main_display_fifo, + int enable_eo_tpacket, + u32 number_of_virtual_channel, + enum nx_mipi_dsi_format format) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + u32 newvalue; + + pregister = __g_pregister[module_index]; + newvalue = (0 << 25); + newvalue |= (enable_auto_flush_main_display_fifo << 29); + newvalue |= (enable_eo_tpacket << 28); + newvalue |= (number_of_virtual_channel << 18); + newvalue |= (format << 12); + writereg(dsim_config, 0xFFFFFF00, newvalue); +} + +void nx_mipi_dsi_set_escape_mode(u32 module_index, u32 stop_state_count, + int force_stop_state, int force_bta, + enum nx_mipi_dsi_lpmode cmdin_lp, + enum nx_mipi_dsi_lpmode txinlp) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + u32 newvalue; + + pregister = __g_pregister[module_index]; + newvalue = (stop_state_count << 21); + newvalue |= (force_stop_state << 20); + newvalue |= (force_bta << 16); + newvalue |= (cmdin_lp << 7); + newvalue |= (txinlp << 6); + writereg(dsim_escmode, 0xFFFFFFC0, newvalue); +} + +void nx_mipi_dsi_set_escape_lp(u32 module_index, + enum nx_mipi_dsi_lpmode cmdin_lp, + enum nx_mipi_dsi_lpmode txinlp) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + u32 newvalue = 0; + + pregister = __g_pregister[module_index]; + newvalue |= (cmdin_lp << 7); + newvalue |= (txinlp << 6); + writereg(dsim_escmode, 0xC0, newvalue); +} + +void nx_mipi_dsi_remote_reset_trigger(u32 module_index) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + u32 newvalue; + + pregister = __g_pregister[module_index]; + newvalue = (1 << 4); + writereg(dsim_escmode, (1 << 4), newvalue); + + while (readl(&pregister->dsim_escmode) & (1 << 4)) + ; +} + +void nx_mipi_dsi_set_ulps(u32 module_index, int ulpsclocklane, int ulpsdatalane) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + + pregister = __g_pregister[module_index]; + regvalue = pregister->dsim_escmode; + + if (ulpsclocklane) { + regvalue &= ~(1 << 0); + regvalue |= (1 << 1); + } else { + regvalue |= (1 << 0); + } + + if (ulpsdatalane) { + regvalue &= ~(1 << 2); + regvalue |= (1 << 3); + } else { + regvalue |= (1 << 2); + } + + writel(regvalue, &pregister->dsim_escmode); + + if (ulpsclocklane) + while ((1 << 9) == + (readl(&pregister->dsim_status) & (1 << 9))) + ; + else + while (0 != (readl(&pregister->dsim_status) & (1 << 9))) + ; + + if (ulpsdatalane) + while ((15 << 4) == + (readl(&pregister->dsim_status) & (15 << 4))) + ; + else + while (0 != (readl(&pregister->dsim_status) & (15 << 4))) + ; + + if (!ulpsclocklane) + regvalue &= (3 << 0); + + if (!ulpsdatalane) + regvalue |= (3 << 2); + + writel(regvalue, &pregister->dsim_escmode); +} + +void nx_mipi_dsi_set_size(u32 module_index, u32 width, u32 height) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + u32 newvalue; + + pregister = __g_pregister[module_index]; + newvalue = (height << 16); + newvalue |= (width << 0); + writereg(dsim_mdresol, 0x0FFFFFFF, newvalue); +} + +void nx_mipi_dsi_set_enable(u32 module_index, int enable) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + + pregister = __g_pregister[module_index]; + writereg(dsim_mdresol, (1 << 31), (enable << 31)); +} + +void nx_mipi_dsi_set_phy(u32 module_index, u32 number_of_data_lanes, + int enable_clock_lane, int enable_data_lane0, + int enable_data_lane1, int enable_data_lane2, + int enable_data_lane3, int swap_clock_lane, + int swap_data_lane) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + u32 newvalue; + + pregister = __g_pregister[module_index]; + newvalue = (number_of_data_lanes << 5); + newvalue |= (enable_clock_lane << 0); + newvalue |= (enable_data_lane0 << 1); + newvalue |= (enable_data_lane1 << 2); + newvalue |= (enable_data_lane2 << 3); + newvalue |= (enable_data_lane3 << 4); + writereg(dsim_config, 0xFF, newvalue); + newvalue = (swap_clock_lane << 1); + newvalue |= (swap_data_lane << 0); + writereg(dsim_phyacchr1, 0x3, newvalue); +} + +void nx_mipi_dsi_set_pll(u32 module_index, int enable, u32 pllstabletimer, + u32 m_pllpms, u32 m_bandctl, u32 m_dphyctl, + u32 b_dphyctl) +{ + register struct nx_mipi_register_set *pregister; + register u32 regvalue; + u32 newvalue; + + pregister = __g_pregister[module_index]; + if (!enable) { + newvalue = (enable << 23); + newvalue |= (m_pllpms << 1); + newvalue |= (m_bandctl << 24); + writereg(dsim_pllctrl, 0x0FFFFFFF, newvalue); + } + + writel(m_dphyctl, &pregister->dsim_phyacchr); + writel(pllstabletimer, &pregister->dsim_plltmr); + writel((b_dphyctl << 9), &pregister->dsim_phyacchr1); + + if (enable) { + newvalue = (enable << 23); + newvalue |= (m_pllpms << 1); + newvalue |= (m_bandctl << 24); + writereg(dsim_pllctrl, 0x0FFFFFFF, newvalue); + } +} + +void nx_mipi_dsi_write_pkheader(u32 module_index, u32 data) +{ + register struct nx_mipi_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(data, &pregister->dsim_pkthdr); +} + +void nx_mipi_dsi_write_payload(u32 module_index, u32 data) +{ + register struct nx_mipi_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(data, &pregister->dsim_payload); +} + +u32 nx_mipi_dsi_read_fifo_status(u32 module_index) +{ + register struct nx_mipi_register_set *pregister; + + pregister = __g_pregister[module_index]; + return readl(&pregister->dsim_fifoctrl); +} diff --git a/drivers/video/nexell/soc/s5pxx18_soc_mipi.h b/drivers/video/nexell/soc/s5pxx18_soc_mipi.h new file mode 100644 index 0000000..63751ca --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_mipi.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _S5PXX18_SOC_MIPI_H_ +#define _S5PXX18_SOC_MIPI_H_ + +#define NUMBER_OF_MIPI_MODULE 1 +#define PHY_BASEADDR_MIPI_MODULE 0xC00D0000 +#define PHY_BASEADDR_MIPI_LIST \ + { PHY_BASEADDR_MIPI_MODULE } + +#define nx_mipi_numberof_csi_channels 2 + +struct nx_mipi_register_set { + u32 csis_control; + u32 csis_dphyctrl; + u32 csis_config_ch0; + u32 csis_dphysts; + u32 csis_intmsk; + u32 csis_intsrc; + u32 csis_ctrl2; + u32 csis_version; + u32 csis_dphyctrl_0; + u32 csis_dphyctrl_1; + u32 __reserved0; + u32 csis_resol_ch0; + u32 __reserved1; + u32 __reserved2; + u32 sdw_config_ch0; + u32 sdw_resol_ch0; + u32 csis_config_ch1; + u32 csis_resol_ch1; + u32 sdw_config_ch1; + u32 sdw_resol_ch1; + u32 csis_config_ch2; + u32 csis_resol_ch2; + u32 sdw_config_ch2; + u32 sdw_resol_ch2; + u32 csis_config_ch3; + u32 csis_resol_ch3; + u32 sdw_config_ch3; + u32 sdw_resol_3; + u32 __reserved3[(16 + 128) / 4]; + + u32 dsim_status; + u32 dsim_swrst; + u32 dsim_clkctrl; + u32 dsim_timeout; + u32 dsim_config; + u32 dsim_escmode; + u32 dsim_mdresol; + u32 dsim_mvporch; + u32 dsim_mhporch; + u32 dsim_msync; + u32 dsim_sdresol; + u32 dsim_intsrc; + u32 dsim_intmsk; + u32 dsim_pkthdr; + u32 dsim_payload; + u32 dsim_rxfifo; + u32 dsim_fifothld; + u32 dsim_fifoctrl; + u32 dsim_memacchr; + u32 dsim_pllctrl; + u32 dsim_plltmr; + u32 dsim_phyacchr; + u32 dsim_phyacchr1; + + u32 __reserved4[(0x2000 - 0x015C) / 4]; + u32 mipi_csis_pktdata[0x2000 / 4]; +}; + +enum nx_mipi_dsi_syncmode { + nx_mipi_dsi_syncmode_event = 0, + nx_mipi_dsi_syncmode_pulse = 1, +}; + +enum nx_mipi_dsi_format { + nx_mipi_dsi_format_command3 = 0, + nx_mipi_dsi_format_command8 = 1, + nx_mipi_dsi_format_command12 = 2, + nx_mipi_dsi_format_command16 = 3, + nx_mipi_dsi_format_rgb565 = 4, + nx_mipi_dsi_format_rgb666_packed = 5, + nx_mipi_dsi_format_rgb666 = 6, + nx_mipi_dsi_format_rgb888 = 7 +}; + +enum nx_mipi_dsi_lpmode { + nx_mipi_dsi_lpmode_hs = 0, + nx_mipi_dsi_lpmode_lp = 1 +}; + +enum nx_mipi_phy_b_dphyctl { + nx_mipi_phy_b_dphyctl_m_txclkesc_20_mhz = 0x1F4, + nx_mipi_phy_b_dphyctl_m_txclkesc_19_mhz = 0x1DB, + nx_mipi_phy_b_dphyctl_m_txclkesc_18_mhz = 0x1C2, + nx_mipi_phy_b_dphyctl_m_txclkesc_17_mhz = 0x1A9, + nx_mipi_phy_b_dphyctl_m_txclkesc_16_mhz = 0x190, + nx_mipi_phy_b_dphyctl_m_txclkesc_15_mhz = 0x177, + nx_mipi_phy_b_dphyctl_m_txclkesc_14_mhz = 0x15E, + nx_mipi_phy_b_dphyctl_m_txclkesc_13_mhz = 0x145, + nx_mipi_phy_b_dphyctl_m_txclkesc_12_mhz = 0x12C, + nx_mipi_phy_b_dphyctl_m_txclkesc_11_mhz = 0x113, + nx_mipi_phy_b_dphyctl_m_txclkesc_10_mhz = 0x0FA, + nx_mipi_phy_b_dphyctl_m_txclkesc_9_mhz = 0x0E1, + nx_mipi_phy_b_dphyctl_m_txclkesc_8_mhz = 0x0C8, + nx_mipi_phy_b_dphyctl_m_txclkesc_7_mhz = 0x0AF, + nx_mipi_phy_b_dphyctl_m_txclkesc_6_mhz = 0x096, + nx_mipi_phy_b_dphyctl_m_txclkesc_5_mhz = 0x07D, + nx_mipi_phy_b_dphyctl_m_txclkesc_4_mhz = 0x064, + nx_mipi_phy_b_dphyctl_m_txclkesc_3_mhz = 0x04B, + nx_mipi_phy_b_dphyctl_m_txclkesc_2_mhz = 0x032, + nx_mipi_phy_b_dphyctl_m_txclkesc_1_mhz = 0x019, + nx_mipi_phy_b_dphyctl_m_txclkesc_0_10_mhz = 0x003, + nx_mipi_phy_b_dphyctl_m_txclkesc_0_01_mhz = 0x000 +}; + +enum { + nx_mipi_rst = 0, + nx_mipi_rst_dsi_i, + nx_mipi_rst_csi_i, + nx_mipi_rst_phy_s, + nx_mipi_rst_phy_m +}; + +enum nx_mipi_int { + nx_mipi_int_csi_even_before = 31, + nx_mipi_int_csi_even_after = 30, + nx_mipi_int_csi_odd_before = 29, + nx_mipi_int_csi_odd_after = 28, + nx_mipi_int_csi_frame_start_ch3 = 27, + nx_mipi_int_csi_frame_start_ch2 = 26, + nx_mipi_int_csi_frame_start_ch1 = 25, + nx_mipi_int_csi_frame_start_ch0 = 24, + nx_mipi_int_csi_frame_end_ch3 = 23, + nx_mipi_int_csi_frame_end_ch2 = 22, + nx_mipi_int_csi_frame_end_ch1 = 21, + nx_mipi_int_csi_frame_end_ch0 = 20, + nx_mipi_int_csi_err_sot_hs_ch3 = 19, + nx_mipi_int_csi_err_sot_hs_ch2 = 18, + nx_mipi_int_csi_err_sot_hs_ch1 = 17, + nx_mipi_int_csi_err_sot_hs_ch0 = 16, + nx_mipi_int_csi_err_lost_fs_ch3 = 15, + nx_mipi_int_csi_err_lost_fs_ch2 = 14, + nx_mipi_int_csi_err_lost_fs_ch1 = 13, + nx_mipi_int_csi_err_lost_fs_ch0 = 12, + nx_mipi_int_csi_err_lost_fe_ch3 = 11, + nx_mipi_int_csi_err_lost_fe_ch2 = 10, + nx_mipi_int_csi_err_lost_fe_ch1 = 9, + nx_mipi_int_csi_err_lost_fe_ch0 = 8, + nx_mipi_int_csi_err_over_ch3 = 7, + nx_mipi_int_csi_err_over_ch2 = 6, + nx_mipi_int_csi_err_over_ch1 = 5, + nx_mipi_int_csi_err_over_ch0 = 4, + + nx_mipi_int_csi_err_ecc = 2, + nx_mipi_int_csi_err_crc = 1, + nx_mipi_int_csi_err_id = 0, + nx_mipi_int_dsi_pll_stable = 32 + 31, + nx_mipi_int_dsi_sw_rst_release = 32 + 30, + nx_mipi_int_dsi_sfrplfifoempty = 32 + 29, + nx_mipi_int_dsi_sfrphfifoempty = 32 + 28, + nx_mipi_int_dsi_sync_override = 32 + 27, + + nx_mipi_int_dsi_bus_turn_over = 32 + 25, + nx_mipi_int_dsi_frame_done = 32 + 24, + + nx_mipi_int_dsi_lpdr_tout = 32 + 21, + nx_mipi_int_dsi_ta_tout = 32 + 20, + + nx_mipi_int_dsi_rx_dat_done = 32 + 18, + nx_mipi_int_dsi_rx_te = 32 + 17, + nx_mipi_int_dsi_rx_ack = 32 + 16, + nx_mipi_int_dsi_err_rx_ecc = 32 + 15, + nx_mipi_int_dsi_err_rx_crc = 32 + 14, + nx_mipi_int_dsi_err_esc3 = 32 + 13, + nx_mipi_int_dsi_err_esc2 = 32 + 12, + nx_mipi_int_dsi_err_esc1 = 32 + 11, + nx_mipi_int_dsi_err_esc0 = 32 + 10, + nx_mipi_int_dsi_err_sync3 = 32 + 9, + nx_mipi_int_dsi_err_sync2 = 32 + 8, + nx_mipi_int_dsi_err_sync1 = 32 + 7, + nx_mipi_int_dsi_err_sync0 = 32 + 6, + nx_mipi_int_dsi_err_control3 = 32 + 5, + nx_mipi_int_dsi_err_control2 = 32 + 4, + nx_mipi_int_dsi_err_control1 = 32 + 3, + nx_mipi_int_dsi_err_control0 = 32 + 2, + nx_mipi_int_dsi_err_content_lp0 = 32 + 1, + nx_mipi_int_dsi_err_content_lp1 = 32 + 0, +}; + +#define DSI_TX_FIFO_SIZE 2048 +#define DSI_RX_FIFO_SIZE 256 +#define DSI_RX_FIFO_EMPTY 0x30800002 + +void nx_mipi_dsi_get_status(u32 module_index, u32 *pulps, u32 *pstop, + u32 *pispllstable, u32 *pisinreset, + u32 *pisbackward, u32 *pishsclockready); + +void nx_mipi_dsi_software_reset(u32 module_index); + +void nx_mipi_dsi_set_clock(u32 module_index, int enable_txhsclock, + int use_external_clock, int enable_byte_clock, + int enable_escclock_clock_lane, + int enable_escclock_data_lane0, + int enable_escclock_data_lane1, + int enable_escclock_data_lane2, + int enable_escclock_data_lane3, + int enable_escprescaler, + u32 escprescalervalue); + +void nx_mipi_dsi_set_timeout(u32 module_index, u32 bta_tout, + u32 lpdrtout); + +void nx_mipi_dsi_set_config_video_mode(u32 module_index, + int enable_auto_flush_main_display_fifo, + int enable_auto_vertical_count, + int enable_burst, + enum nx_mipi_dsi_syncmode + sync_mode, int enable_eo_tpacket, + int enable_hsync_end_packet, + int enable_hfp, int enable_hbp, + int enable_hsa, + u32 number_of_virtual_channel, + enum nx_mipi_dsi_format format, + u32 number_of_words_in_hfp, + u32 number_of_words_in_hbp, + u32 number_of_words_in_hsync, + u32 number_of_lines_in_vfp, + u32 number_of_lines_in_vbp, + u32 number_of_lines_in_vsync, + u32 number_of_lines_in_command_allow); + +void nx_mipi_dsi_set_config_command_mode(u32 module_index, + int enable_auto_flush_main_display_fifo, + int enable_eo_tpacket, + u32 number_of_virtual_channel, + enum nx_mipi_dsi_format format); + +void nx_mipi_dsi_set_escape_mode(u32 module_index, u32 stop_state_count, + int force_stop_state, int force_bta, + enum nx_mipi_dsi_lpmode cmdin_lp, + enum nx_mipi_dsi_lpmode txinlp); +void nx_mipi_dsi_set_escape_lp(u32 module_index, + enum nx_mipi_dsi_lpmode cmdin_lp, + enum nx_mipi_dsi_lpmode txinlp); + +void nx_mipi_dsi_remote_reset_trigger(u32 module_index); +void nx_mipi_dsi_set_ulps(u32 module_index, int ulpsclocklane, + int ulpsdatalane); +void nx_mipi_dsi_set_size(u32 module_index, u32 width, u32 height); +void nx_mipi_dsi_set_enable(u32 module_index, int enable); +void nx_mipi_dsi_set_phy(u32 module_index, u32 number_of_data_lanes, + int enable_clock_lane, int enable_data_lane0, + int enable_data_lane1, int enable_data_lane2, + int enable_data_lane3, int swap_clock_lane, + int swap_data_lane); + +void nx_mipi_dsi_set_pll(u32 module_index, int enable, + u32 pllstabletimer, u32 m_pllpms, u32 m_bandctl, + u32 m_dphyctl, u32 b_dphyctl); + +void nx_mipi_dsi_write_pkheader(u32 module_index, u32 data); +void nx_mipi_dsi_write_payload(u32 module_index, u32 data); +u32 nx_mipi_dsi_read_fifo(u32 module_index); +u32 nx_mipi_dsi_read_fifo_status(u32 module_index); + +int nx_mipi_smoke_test(u32 module_index); +void nx_mipi_set_base_address(u32 module_index, void *base_address); +void *nx_mipi_get_base_address(u32 module_index); +u32 nx_mipi_get_physical_address(u32 module_index); + +void nx_mipi_dsi_set_interrupt_enable_all(u32 module_index, int enable); +void nx_mipi_dsi_set_interrupt_enable(u32 module_index, + u32 int_num, int enable); +int nx_mipi_dsi_get_interrupt_enable(u32 module_index, u32 int_num); +int nx_mipi_dsi_get_interrupt_enable_all(u32 module_index); + +int nx_mipi_dsi_get_interrupt_pending(u32 module_index, u32 int_num); +int nx_mipi_dsi_get_interrupt_pending_all(u32 module_index); +int32_t nx_mipi_dsi_get_interrupt_pending_number(u32 module_index); + +void nx_mipi_dsi_clear_interrupt_pending(u32 module_index, u32 int_num); +void nx_mipi_dsi_clear_interrupt_pending_all(u32 module_index); + +#endif diff --git a/drivers/video/nexell/soc/s5pxx18_soc_mlc.c b/drivers/video/nexell/soc/s5pxx18_soc_mlc.c new file mode 100644 index 0000000..c8cf833 --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_mlc.c @@ -0,0 +1,1861 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <linux/types.h> +#include <linux/io.h> + +#include "s5pxx18_soc_mlc.h" + +static struct { + struct nx_mlc_register_set *pregister; +} __g_module_variables[NUMBER_OF_MLC_MODULE] = { { NULL, },}; + +int nx_mlc_initialize(void) +{ + static int binit; + u32 i; + + if (binit == 0) { + for (i = 0; i < NUMBER_OF_MLC_MODULE; i++) + __g_module_variables[i].pregister = NULL; + binit = 1; + } + return 1; +} + +u32 nx_mlc_get_physical_address(u32 module_index) +{ + const u32 physical_addr[] = PHY_BASEADDR_MLC_LIST; + + return physical_addr[module_index]; +} + +void nx_mlc_set_base_address(u32 module_index, void *base_address) +{ + __g_module_variables[module_index].pregister = + (struct nx_mlc_register_set *)base_address; +} + +void *nx_mlc_get_base_address(u32 module_index) +{ + return (void *)__g_module_variables[module_index].pregister; +} + +void nx_mlc_set_clock_pclk_mode(u32 module_index, enum nx_pclkmode mode) +{ + const u32 pclkmode_pos = 3; + u32 clkmode = 0; + + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + switch (mode) { + case nx_pclkmode_dynamic: + clkmode = 0; + break; + case nx_pclkmode_always: + clkmode = 1; + break; + default: + break; + } + regvalue = pregister->mlcclkenb; + regvalue &= ~(1ul << pclkmode_pos); + regvalue |= (clkmode & 0x01) << pclkmode_pos; + + writel(regvalue, &pregister->mlcclkenb); +} + +enum nx_pclkmode nx_mlc_get_clock_pclk_mode(u32 module_index) +{ + const u32 pclkmode_pos = 3; + + if (__g_module_variables[module_index].pregister->mlcclkenb & + (1ul << pclkmode_pos)) { + return nx_pclkmode_always; + } + return nx_pclkmode_dynamic; +} + +void nx_mlc_set_clock_bclk_mode(u32 module_index, enum nx_bclkmode mode) +{ + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + u32 clkmode = 0; + + pregister = __g_module_variables[module_index].pregister; + switch (mode) { + case nx_bclkmode_disable: + clkmode = 0; + break; + case nx_bclkmode_dynamic: + clkmode = 2; + break; + case nx_bclkmode_always: + clkmode = 3; + break; + default: + break; + } + regvalue = pregister->mlcclkenb; + regvalue &= ~(0x3); + regvalue |= clkmode & 0x3; + + writel(regvalue, &pregister->mlcclkenb); +} + +enum nx_bclkmode nx_mlc_get_clock_bclk_mode(u32 module_index) +{ + const u32 bclkmode = 3ul << 0; + + switch (__g_module_variables[module_index].pregister->mlcclkenb & + bclkmode) { + case 0: + return nx_bclkmode_disable; + case 2: + return nx_bclkmode_dynamic; + case 3: + return nx_bclkmode_always; + } + return nx_bclkmode_disable; +} + +void nx_mlc_set_top_power_mode(u32 module_index, int bpower) +{ + const u32 pixelbuffer_pwd_pos = 11; + const u32 pixelbuffer_pwd_mask = 1ul << pixelbuffer_pwd_pos; + const u32 dittyflag_mask = 1ul << 3; + register struct nx_mlc_register_set *pregister; + register u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->mlccontrolt; + regvalue &= ~(pixelbuffer_pwd_mask | dittyflag_mask); + regvalue |= (bpower << pixelbuffer_pwd_pos); + + writel(regvalue, &pregister->mlccontrolt); +} + +int nx_mlc_get_top_power_mode(u32 module_index) +{ + const u32 pixelbuffer_pwd_pos = 11; + const u32 pixelbuffer_pwd_mask = 1ul << pixelbuffer_pwd_pos; + + return (int)((__g_module_variables[module_index].pregister->mlccontrolt + & pixelbuffer_pwd_mask) >> + pixelbuffer_pwd_pos); +} + +void nx_mlc_set_top_sleep_mode(u32 module_index, int bsleep) +{ + const u32 pixelbuffer_sld_pos = 10; + const u32 pixelbuffer_sld_mask = 1ul << pixelbuffer_sld_pos; + const u32 dittyflag_mask = 1ul << 3; + register struct nx_mlc_register_set *pregister; + register u32 regvalue; + + bsleep = (int)((u32)bsleep ^ 1); + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->mlccontrolt; + regvalue &= ~(pixelbuffer_sld_mask | dittyflag_mask); + regvalue |= (bsleep << pixelbuffer_sld_pos); + + writel(regvalue, &pregister->mlccontrolt); +} + +int nx_mlc_get_top_sleep_mode(u32 module_index) +{ + const u32 pixelbuffer_sld_pos = 11; + const u32 pixelbuffer_sld_mask = 1ul << pixelbuffer_sld_pos; + + return (int)(((__g_module_variables[module_index].pregister->mlccontrolt + & pixelbuffer_sld_mask) >> + pixelbuffer_sld_pos) ^ 0x01); +} + +void nx_mlc_set_top_dirty_flag(u32 module_index) +{ + const u32 dirtyflag = 1ul << 3; + register struct nx_mlc_register_set *pregister; + register u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->mlccontrolt; + regvalue |= dirtyflag; + + writel(regvalue, &pregister->mlccontrolt); +} + +int nx_mlc_get_top_dirty_flag(u32 module_index) +{ + const u32 dirtyflag_pos = 3; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + + return (int)((readl(&__g_module_variables[module_index] + .pregister->mlccontrolt) & + dirtyflag_mask) >> dirtyflag_pos); +} + +void nx_mlc_set_mlc_enable(u32 module_index, int benb) +{ + const u32 mlcenb_pos = 1; + const u32 mlcenb_mask = 1ul << mlcenb_pos; + const u32 dirtyflag_pos = 3; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->mlccontrolt; + regvalue &= ~(mlcenb_mask | dirtyflag_mask); + regvalue |= (benb << mlcenb_pos); + + writel(regvalue, &pregister->mlccontrolt); +} + +int nx_mlc_get_mlc_enable(u32 module_index) +{ + const u32 mlcenb_pos = 1; + const u32 mlcenb_mask = 1ul << mlcenb_pos; + + return (int)((__g_module_variables[module_index].pregister->mlccontrolt + & mlcenb_mask) >> mlcenb_pos); +} + +void nx_mlc_set_field_enable(u32 module_index, int benb) +{ + const u32 fieldenb_pos = 0; + const u32 fieldenb_mask = 1ul << fieldenb_pos; + const u32 dirtyflag_pos = 3; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->mlccontrolt; + regvalue &= ~(fieldenb_mask | dirtyflag_mask); + regvalue |= (benb << fieldenb_pos); + + writel(regvalue, &pregister->mlccontrolt); +} + +int nx_mlc_get_field_enable(u32 module_index) +{ + const u32 fieldenb_pos = 0; + const u32 fieldenb_mask = 1ul << fieldenb_pos; + + return (int)(__g_module_variables[module_index].pregister->mlccontrolt & + fieldenb_mask); +} + +void nx_mlc_set_layer_priority(u32 module_index, enum nx_mlc_priority priority) +{ + const u32 priority_pos = 8; + const u32 priority_mask = 0x03 << priority_pos; + const u32 dirtyflag_pos = 3; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + register struct nx_mlc_register_set *pregister; + register u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->mlccontrolt; + regvalue &= ~(priority_mask | dirtyflag_mask); + regvalue |= (priority << priority_pos); + + writel(regvalue, &pregister->mlccontrolt); +} + +void nx_mlc_set_screen_size(u32 module_index, u32 width, u32 height) +{ + register struct nx_mlc_register_set *pregister; + register u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + regvalue = ((height - 1) << 16) | (width - 1); + + writel(regvalue, &pregister->mlcscreensize); +} + +void nx_mlc_get_screen_size(u32 module_index, u32 *pwidth, u32 *pheight) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + if (pwidth) + *pwidth = (pregister->mlcscreensize & 0x0fff) + 1; + + if (pheight) + *pheight = ((pregister->mlcscreensize >> 16) & 0x0fff) + 1; +} + +void nx_mlc_set_background(u32 module_index, u32 color) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(color, &pregister->mlcbgcolor); +} + +void nx_mlc_set_dirty_flag(u32 module_index, u32 layer) +{ + register struct nx_mlc_register_set *pregister; + register u32 regvalue; + const u32 dirtyflg_mask = 1ul << 4; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + regvalue = pregister->mlcrgblayer[layer].mlccontrol; + regvalue |= dirtyflg_mask; + + writel(regvalue, &pregister->mlcrgblayer[layer].mlccontrol); + } else if (layer == 3) { + regvalue = pregister->mlcvideolayer.mlccontrol; + regvalue |= dirtyflg_mask; + + writel(regvalue, &pregister->mlcvideolayer.mlccontrol); + } +} + +int nx_mlc_get_dirty_flag(u32 module_index, u32 layer) +{ + const u32 dirtyflg_pos = 4; + const u32 dirtyflg_mask = 1ul << dirtyflg_pos; + + if (layer == 0 || layer == 1) { + return (int)((__g_module_variables[module_index] + .pregister->mlcrgblayer[layer] + .mlccontrol & dirtyflg_mask) >> dirtyflg_pos); + } else if (layer == 2) { + return (int)((__g_module_variables[module_index] + .pregister->mlcrgblayer2.mlccontrol & + dirtyflg_mask) >> dirtyflg_pos); + } else if (layer == 3) { + return (int)((__g_module_variables[module_index] + .pregister->mlcvideolayer.mlccontrol & + dirtyflg_mask) >> dirtyflg_pos); + } + return 0; +} + +void nx_mlc_set_layer_enable(u32 module_index, u32 layer, int benb) +{ + const u32 layerenb_pos = 5; + const u32 layerenb_mask = 0x01 << layerenb_pos; + const u32 dirtyflag_pos = 4; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + regvalue = pregister->mlcrgblayer[layer].mlccontrol; + regvalue &= ~(layerenb_mask | dirtyflag_mask); + regvalue |= (benb << layerenb_pos); + + writel(regvalue, &pregister->mlcrgblayer[layer].mlccontrol); + } else if (layer == 3) { + regvalue = pregister->mlcvideolayer.mlccontrol; + regvalue &= ~(layerenb_mask | dirtyflag_mask); + regvalue |= (benb << layerenb_pos); + + writel(regvalue, &pregister->mlcvideolayer.mlccontrol); + } +} + +int nx_mlc_get_layer_enable(u32 module_index, u32 layer) +{ + const u32 layerenb_pos = 5; + const u32 layerenb_mask = 0x01 << layerenb_pos; + + if (layer == 0 || layer == 1) { + return (int)((__g_module_variables[module_index] + .pregister->mlcrgblayer[layer] + .mlccontrol & layerenb_mask) >> layerenb_pos); + } else if (layer == 3) { + return (int)((__g_module_variables[module_index] + .pregister->mlcvideolayer.mlccontrol & + layerenb_mask) >> layerenb_pos); + } + return 0; +} + +void nx_mlc_set_lock_size(u32 module_index, u32 layer, u32 locksize) +{ + const u32 locksize_mask = 3ul << 12; + const u32 dirtyflag_pos = 4; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + register struct nx_mlc_register_set *pregister; + register u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + locksize >>= 3; + if (layer == 0 || layer == 1) { + regvalue = pregister->mlcrgblayer[layer].mlccontrol; + regvalue &= ~(locksize_mask | dirtyflag_mask); + regvalue |= (locksize << 12); + + writel(regvalue, &pregister->mlcrgblayer[layer].mlccontrol); + } +} + +void nx_mlc_set_alpha_blending(u32 module_index, u32 layer, int benb, u32 alpha) +{ + const u32 blendenb_pos = 2; + const u32 blendenb_mask = 0x01 << blendenb_pos; + const u32 dirtyflag_pos = 4; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + const u32 alpha_pos = 28; + const u32 alpha_mask = 0xf << alpha_pos; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + regvalue = pregister->mlcrgblayer[layer].mlccontrol; + regvalue &= ~(blendenb_mask | dirtyflag_mask); + regvalue |= (benb << blendenb_pos); + + writel(regvalue, &pregister->mlcrgblayer[layer].mlccontrol); + regvalue = pregister->mlcrgblayer[layer].mlctpcolor; + regvalue &= ~alpha_mask; + regvalue |= alpha << alpha_pos; + + writel(regvalue, &pregister->mlcrgblayer[layer].mlctpcolor); + } else if (layer == 3) { + regvalue = pregister->mlcvideolayer.mlccontrol; + regvalue &= ~(blendenb_mask | dirtyflag_mask); + regvalue |= (benb << blendenb_pos); + + writel(regvalue, &pregister->mlcvideolayer.mlccontrol); + + writel(alpha << alpha_pos, + &pregister->mlcvideolayer.mlctpcolor); + } +} + +void nx_mlc_set_transparency(u32 module_index, u32 layer, int benb, u32 color) +{ + const u32 tpenb_pos = 0; + const u32 tpenb_mask = 0x01 << tpenb_pos; + const u32 dirtyflag_pos = 4; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + const u32 tpcolor_pos = 0; + const u32 tpcolor_mask = ((1 << 24) - 1) << tpcolor_pos; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + regvalue = pregister->mlcrgblayer[layer].mlccontrol; + regvalue &= ~(tpenb_mask | dirtyflag_mask); + regvalue |= (benb << tpenb_pos); + + writel(regvalue, &pregister->mlcrgblayer[layer].mlccontrol); + regvalue = pregister->mlcrgblayer[layer].mlctpcolor; + regvalue &= ~tpcolor_mask; + regvalue |= (color & tpcolor_mask); + + writel(regvalue, &pregister->mlcrgblayer[layer].mlctpcolor); + } +} + +void nx_mlc_set_color_inversion(u32 module_index, u32 layer, int benb, + u32 color) +{ + const u32 invenb_pos = 1; + const u32 invenb_mask = 0x01 << invenb_pos; + const u32 dirtyflag_pos = 4; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + const u32 invcolor_pos = 0; + const u32 invcolor_mask = ((1 << 24) - 1) << invcolor_pos; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + regvalue = pregister->mlcrgblayer[layer].mlccontrol; + regvalue &= ~(invenb_mask | dirtyflag_mask); + regvalue |= (benb << invenb_pos); + + writel(regvalue, &pregister->mlcrgblayer[layer].mlccontrol); + regvalue = pregister->mlcrgblayer[layer].mlcinvcolor; + regvalue &= ~invcolor_mask; + regvalue |= (color & invcolor_mask); + + writel(regvalue, &pregister->mlcrgblayer[layer].mlcinvcolor); + } +} + +u32 nx_mlc_get_extended_color(u32 module_index, u32 color, + enum nx_mlc_rgbfmt format) +{ + u32 rgb[3] = { + 0, + }; + u32 bw[3] = { + 0, + }; + u32 bp[3] = { + 0, + }; + u32 blank = 0; + u32 fill = 0; + u32 i = 0; + + switch (format) { + case nx_mlc_rgbfmt_r5g6b5: + bw[0] = 5; + bw[1] = 6; + bw[2] = 5; + bp[0] = 11; + bp[1] = 5; + bp[2] = 0; + break; + case nx_mlc_rgbfmt_b5g6r5: + bw[0] = 5; + bw[1] = 6; + bw[2] = 5; + bp[0] = 0; + bp[1] = 5; + bp[2] = 11; + break; + case nx_mlc_rgbfmt_x1r5g5b5: + case nx_mlc_rgbfmt_a1r5g5b5: + bw[0] = 5; + bw[1] = 5; + bw[2] = 5; + bp[0] = 10; + bp[1] = 5; + bp[2] = 0; + break; + case nx_mlc_rgbfmt_x1b5g5r5: + case nx_mlc_rgbfmt_a1b5g5r5: + bw[0] = 5; + bw[1] = 5; + bw[2] = 5; + bp[0] = 0; + bp[1] = 5; + bp[2] = 10; + break; + case nx_mlc_rgbfmt_x4r4g4b4: + case nx_mlc_rgbfmt_a4r4g4b4: + bw[0] = 4; + bw[1] = 4; + bw[2] = 4; + bp[0] = 8; + bp[1] = 4; + bp[2] = 0; + break; + case nx_mlc_rgbfmt_x4b4g4r4: + case nx_mlc_rgbfmt_a4b4g4r4: + bw[0] = 4; + bw[1] = 4; + bw[2] = 4; + bp[0] = 0; + bp[1] = 4; + bp[2] = 8; + break; + case nx_mlc_rgbfmt_x8r3g3b2: + case nx_mlc_rgbfmt_a8r3g3b2: + bw[0] = 3; + bw[1] = 3; + bw[2] = 2; + bp[0] = 5; + bp[1] = 2; + bp[2] = 0; + break; + case nx_mlc_rgbfmt_x8b3g3r2: + case nx_mlc_rgbfmt_a8b3g3r2: + bw[0] = 2; + bw[1] = 3; + bw[2] = 3; + bp[0] = 0; + bp[1] = 2; + bp[2] = 5; + break; + case nx_mlc_rgbfmt_r8g8b8: + case nx_mlc_rgbfmt_a8r8g8b8: + bw[0] = 8; + bw[1] = 8; + bw[2] = 8; + bp[0] = 16; + bp[1] = 8; + bp[2] = 0; + break; + case nx_mlc_rgbfmt_b8g8r8: + case nx_mlc_rgbfmt_a8b8g8r8: + bw[0] = 8; + bw[1] = 8; + bw[2] = 8; + bp[0] = 0; + bp[1] = 8; + bp[2] = 16; + break; + default: + break; + } + for (i = 0; i < 3; i++) { + rgb[i] = (color >> bp[i]) & ((u32)(1 << bw[i]) - 1); + fill = bw[i]; + blank = 8 - fill; + rgb[i] <<= blank; + while (blank > 0) { + rgb[i] |= (rgb[i] >> fill); + blank -= fill; + fill += fill; + } + } + + return (rgb[0] << 16) | (rgb[1] << 8) | (rgb[2] << 0); +} + +void nx_mlc_set_format_rgb(u32 module_index, u32 layer, + enum nx_mlc_rgbfmt format) +{ + const u32 dirtyflag_pos = 4; + const u32 dirtyflag_mask = 1ul << dirtyflag_pos; + const u32 format_mask = 0xffff0000ul; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + regvalue = pregister->mlcrgblayer[layer].mlccontrol; + regvalue &= ~(format_mask | dirtyflag_mask); + regvalue |= (u32)format; + + writel(regvalue, &pregister->mlcrgblayer[layer].mlccontrol); + } +} + +void nx_mlc_set_format_yuv(u32 module_index, enum nx_mlc_yuvfmt format) +{ + const u32 format_mask = 0xffff0000ul; + register u32 temp; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + temp = pregister->mlcvideolayer.mlccontrol; + temp &= ~format_mask; + temp |= (u32)format; + + writel(temp, &pregister->mlcvideolayer.mlccontrol); +} + +void nx_mlc_set_position(u32 module_index, u32 layer, s32 sx, s32 sy, + s32 ex, s32 ey) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + writel((((u32)sx & 0xffful) << 16) | ((u32)ex & 0xffful), + &pregister->mlcrgblayer[layer].mlcleftright); + + writel((((u32)sy & 0xffful) << 16) | ((u32)ey & 0xffful), + &pregister->mlcrgblayer[layer].mlctopbottom); + } else if (layer == 2) { + writel((((u32)sx & 0xffful) << 16) | ((u32)ex & 0xffful), + &pregister->mlcrgblayer2.mlcleftright); + + writel((((u32)sy & 0xffful) << 16) | ((u32)ey & 0xffful), + &pregister->mlcrgblayer2.mlctopbottom); + } else if (layer == 3) { + writel((((u32)sx & 0xffful) << 16) | ((u32)ex & 0xffful), + &pregister->mlcvideolayer.mlcleftright); + + writel((((u32)sy & 0xffful) << 16) | ((u32)ey & 0xffful), + &pregister->mlcvideolayer.mlctopbottom); + } +} + +void nx_mlc_set_dither_enable_when_using_gamma(u32 module_index, int benable) +{ + const u32 ditherenb_bitpos = 0; + const u32 ditherenb_mask = 1 << ditherenb_bitpos; + register struct nx_mlc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + read_value &= ~ditherenb_mask; + read_value |= ((u32)benable << ditherenb_bitpos); + + writel(read_value, &pregister->mlcgammacont); +} + +int nx_mlc_get_dither_enable_when_using_gamma(u32 module_index) +{ + const u32 ditherenb_bitpos = 0; + const u32 ditherenb_mask = 1 << ditherenb_bitpos; + + return (int)(__g_module_variables[module_index].pregister->mlcgammacont + & ditherenb_mask); +} + +void nx_mlc_set_gamma_priority(u32 module_index, int bvideolayer) +{ + const u32 alphaselect_bitpos = 5; + const u32 alphaselect_mask = 1 << alphaselect_bitpos; + register struct nx_mlc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + read_value &= ~alphaselect_mask; + read_value |= ((u32)bvideolayer << alphaselect_bitpos); + + writel(read_value, &pregister->mlcgammacont); +} + +int nx_mlc_get_gamma_priority(u32 module_index) +{ + const u32 alphaselect_bitpos = 5; + const u32 alphaselect_mask = 1 << alphaselect_bitpos; + + return (int)((__g_module_variables[module_index].pregister->mlcgammacont + & alphaselect_mask) >> alphaselect_bitpos); +} + +void nx_mlc_set_rgblayer_invalid_position(u32 module_index, u32 layer, + u32 region, s32 sx, s32 sy, + s32 ex, s32 ey, int benb) +{ + const u32 invalidenb_pos = 28; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + if (region == 0) { + writel(((benb << invalidenb_pos) | + ((sx & 0x7ff) << 16) | (ex & 0x7ff)), + &pregister->mlcrgblayer[layer] + .mlcinvalidleftright0); + + writel((((sy & 0x7ff) << 16) | (ey & 0x7ff)), + &pregister->mlcrgblayer[layer] + .mlcinvalidtopbottom0); + } else { + writel(((benb << invalidenb_pos) | + ((sx & 0x7ff) << 16) | (ex & 0x7ff)), + &pregister->mlcrgblayer[layer] + .mlcinvalidleftright1); + + writel((((sy & 0x7ff) << 16) | (ey & 0x7ff)), + &pregister->mlcrgblayer[layer] + .mlcinvalidtopbottom1); + } + } +} + +void nx_mlc_set_rgblayer_stride(u32 module_index, u32 layer, s32 hstride, + s32 vstride) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) { + writel(hstride, &pregister->mlcrgblayer[layer].mlchstride); + writel(vstride, &pregister->mlcrgblayer[layer].mlcvstride); + } else if (layer == 2) { + writel(hstride, &pregister->mlcrgblayer2.mlchstride); + writel(vstride, &pregister->mlcrgblayer2.mlcvstride); + } +} + +void nx_mlc_set_rgblayer_address(u32 module_index, u32 layer, u32 addr) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0 || layer == 1) + writel(addr, &pregister->mlcrgblayer[layer].mlcaddress); + else if (layer == 2) + writel(addr, &pregister->mlcrgblayer2.mlcaddress); +} + +void nx_mlc_set_rgblayer_gama_table_power_mode(u32 module_index, int bred, + int bgreen, int bblue) +{ + const u32 bgammatable_pwd_bitpos = 11; + const u32 ggammatable_pwd_bitpos = 9; + const u32 rgammatable_pwd_bitpos = 3; + const u32 bgammatable_pwd_mask = (1 << bgammatable_pwd_bitpos); + const u32 ggammatable_pwd_mask = (1 << ggammatable_pwd_bitpos); + const u32 rgammatable_pwd_mask = (1 << rgammatable_pwd_bitpos); + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + read_value &= ~(bgammatable_pwd_mask | ggammatable_pwd_mask | + rgammatable_pwd_mask); + read_value |= (((u32)bred << rgammatable_pwd_bitpos) | + ((u32)bgreen << ggammatable_pwd_bitpos) | + ((u32)bblue << bgammatable_pwd_bitpos)); + + writel(read_value, &pregister->mlcgammacont); +} + +void nx_mlc_get_rgblayer_gama_table_power_mode(u32 module_index, int *pbred, + int *pbgreen, int *pbblue) +{ + const u32 bgammatable_pwd_bitpos = 11; + const u32 ggammatable_pwd_bitpos = 9; + const u32 rgammatable_pwd_bitpos = 3; + const u32 bgammatable_pwd_mask = (1 << bgammatable_pwd_bitpos); + const u32 ggammatable_pwd_mask = (1 << ggammatable_pwd_bitpos); + const u32 rgammatable_pwd_mask = (1 << rgammatable_pwd_bitpos); + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + if (pbred) + *pbred = (read_value & rgammatable_pwd_mask) ? 1 : 0; + + if (pbgreen) + *pbgreen = (read_value & ggammatable_pwd_mask) ? 1 : 0; + + if (pbblue) + *pbblue = (read_value & bgammatable_pwd_mask) ? 1 : 0; +} + +void nx_mlc_set_rgblayer_gama_table_sleep_mode(u32 module_index, int bred, + int bgreen, int bblue) +{ + const u32 bgammatable_sld_bitpos = 10; + const u32 ggammatable_sld_bitpos = 8; + const u32 rgammatable_sld_bitpos = 2; + const u32 bgammatable_sld_mask = (1 << bgammatable_sld_bitpos); + const u32 ggammatable_sld_mask = (1 << ggammatable_sld_bitpos); + const u32 rgammatable_sld_mask = (1 << rgammatable_sld_bitpos); + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + if (bred) + read_value &= ~rgammatable_sld_mask; + else + read_value |= rgammatable_sld_mask; + + if (bgreen) + read_value &= ~ggammatable_sld_mask; + else + read_value |= ggammatable_sld_mask; + + if (bblue) + read_value &= ~bgammatable_sld_mask; + else + read_value |= bgammatable_sld_mask; + + writel(read_value, &pregister->mlcgammacont); +} + +void nx_mlc_get_rgblayer_gama_table_sleep_mode(u32 module_index, int *pbred, + int *pbgreen, int *pbblue) +{ + const u32 bgammatable_sld_bitpos = 10; + const u32 ggammatable_sld_bitpos = 8; + const u32 rgammatable_sld_bitpos = 2; + const u32 bgammatable_sld_mask = (1 << bgammatable_sld_bitpos); + const u32 ggammatable_sld_mask = (1 << ggammatable_sld_bitpos); + const u32 rgammatable_sld_mask = (1 << rgammatable_sld_bitpos); + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + + if (pbred) + *pbred = (read_value & rgammatable_sld_mask) ? 0 : 1; + + if (pbgreen) + *pbgreen = (read_value & ggammatable_sld_mask) ? 0 : 1; + + if (pbblue) + *pbblue = (read_value & bgammatable_sld_mask) ? 0 : 1; +} + +void nx_mlc_set_rgblayer_rgamma_table(u32 module_index, u32 dwaddress, + u32 dwdata) +{ + register struct nx_mlc_register_set *pregister; + const u32 tableaddr_bitpos = 24; + + pregister = __g_module_variables[module_index].pregister; + writel(((dwaddress << tableaddr_bitpos) | dwdata), + &pregister->mlcrgammatablewrite); +} + +void nx_mlc_set_rgblayer_ggamma_table(u32 module_index, u32 dwaddress, + u32 dwdata) +{ + register struct nx_mlc_register_set *pregister; + const u32 tableaddr_bitpos = 24; + + pregister = __g_module_variables[module_index].pregister; + writel(((dwaddress << tableaddr_bitpos) | dwdata), + &pregister->mlcggammatablewrite); +} + +void nx_mlc_set_rgblayer_bgamma_table(u32 module_index, u32 dwaddress, + u32 dwdata) +{ + register struct nx_mlc_register_set *pregister; + const u32 tableaddr_bitpos = 24; + + pregister = __g_module_variables[module_index].pregister; + writel(((dwaddress << tableaddr_bitpos) | dwdata), + &pregister->mlcbgammatablewrite); +} + +void nx_mlc_set_rgblayer_gamma_enable(u32 module_index, int benable) +{ + const u32 rgbgammaemb_bitpos = 1; + const u32 rgbgammaemb_mask = 1 << rgbgammaemb_bitpos; + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + read_value &= ~rgbgammaemb_mask; + read_value |= (u32)benable << rgbgammaemb_bitpos; + + writel(read_value, &pregister->mlcgammacont); +} + +int nx_mlc_get_rgblayer_gamma_enable(u32 module_index) +{ + const u32 rgbgammaemb_bitpos = 1; + const u32 rgbgammaemb_mask = 1 << rgbgammaemb_bitpos; + + return (int)((__g_module_variables[module_index].pregister->mlcgammacont + & rgbgammaemb_mask) >> rgbgammaemb_bitpos); +} + +void nx_mlc_set_video_layer_stride(u32 module_index, s32 lu_stride, + s32 cb_stride, s32 cr_stride) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + writel(lu_stride, &pregister->mlcvideolayer.mlcvstride); + writel(cb_stride, &pregister->mlcvideolayer.mlcvstridecb); + writel(cr_stride, &pregister->mlcvideolayer.mlcvstridecr); +} + +void nx_mlc_set_video_layer_address(u32 module_index, u32 lu_addr, u32 cb_addr, + u32 cr_addr) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(lu_addr, &pregister->mlcvideolayer.mlcaddress); + writel(cb_addr, &pregister->mlcvideolayer.mlcaddresscb); + writel(cr_addr, &pregister->mlcvideolayer.mlcaddresscr); +} + +void nx_mlc_set_video_layer_address_yuyv(u32 module_index, u32 addr, + s32 stride) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(addr, &pregister->mlcvideolayer.mlcaddress); + writel(stride, &pregister->mlcvideolayer.mlcvstride); +} + +void nx_mlc_set_video_layer_scale_factor(u32 module_index, u32 hscale, + u32 vscale, int bhlumaenb, + int bhchromaenb, int bvlumaenb, + int bvchromaenb) +{ + const u32 filter_luma_pos = 28; + const u32 filter_choma_pos = 29; + const u32 scale_mask = ((1 << 23) - 1); + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + writel(((bhlumaenb << filter_luma_pos) | + (bhchromaenb << filter_choma_pos) | (hscale & scale_mask)), + &pregister->mlcvideolayer.mlchscale); + + writel(((bvlumaenb << filter_luma_pos) | + (bvchromaenb << filter_choma_pos) | (vscale & scale_mask)), + &pregister->mlcvideolayer.mlcvscale); +} + +void nx_mlc_set_video_layer_scale_filter(u32 module_index, int bhlumaenb, + int bhchromaenb, int bvlumaenb, + int bvchromaenb) +{ + const u32 filter_luma_pos = 28; + const u32 filter_choma_pos = 29; + const u32 scale_mask = ((1 << 23) - 1); + register struct nx_mlc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcvideolayer.mlchscale; + read_value &= scale_mask; + read_value |= + (bhlumaenb << filter_luma_pos) | (bhchromaenb << filter_choma_pos); + + writel(read_value, &pregister->mlcvideolayer.mlchscale); + read_value = pregister->mlcvideolayer.mlcvscale; + read_value &= scale_mask; + read_value |= + (bvlumaenb << filter_luma_pos) | (bvchromaenb << filter_choma_pos); + + writel(read_value, &pregister->mlcvideolayer.mlcvscale); +} + +void nx_mlc_get_video_layer_scale_filter(u32 module_index, int *bhlumaenb, + int *bhchromaenb, int *bvlumaenb, + int *bvchromaenb) +{ + const u32 filter_luma_pos = 28; + const u32 filter_choma_pos = 29; + const u32 filter_mask = 1ul; + register struct nx_mlc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcvideolayer.mlchscale; + *bhlumaenb = (read_value >> filter_luma_pos) & filter_mask; + *bhchromaenb = (read_value >> filter_choma_pos) & filter_mask; + read_value = pregister->mlcvideolayer.mlcvscale; + *bvlumaenb = (read_value >> filter_luma_pos) & filter_mask; + *bvchromaenb = (read_value >> filter_choma_pos) & filter_mask; +} + +void nx_mlc_set_video_layer_scale(u32 module_index, u32 sw, u32 sh, u32 dw, + u32 dh, int bhlumaenb, int bhchromaenb, + int bvlumaenb, int bvchromaenb) +{ + const u32 filter_luma_pos = 28; + const u32 filter_choma_pos = 29; + const u32 scale_mask = ((1 << 23) - 1); + register u32 hscale, vscale, cal_sh; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + if ((bhlumaenb || bhchromaenb) && dw > sw) { + sw--; + dw--; + } + hscale = (sw << 11) / dw; + + if ((bvlumaenb || bvchromaenb) && dh > sh) { + sh--; + dh--; + vscale = (sh << 11) / dh; + + cal_sh = ((vscale * dh) >> 11); + if (sh <= cal_sh) + vscale--; + + } else { + vscale = (sh << 11) / dh; + } + + writel(((bhlumaenb << filter_luma_pos) | + (bhchromaenb << filter_choma_pos) | (hscale & scale_mask)), + &pregister->mlcvideolayer.mlchscale); + + writel(((bvlumaenb << filter_luma_pos) | + (bvchromaenb << filter_choma_pos) | (vscale & scale_mask)), + &pregister->mlcvideolayer.mlcvscale); +} + +void nx_mlc_set_video_layer_luma_enhance(u32 module_index, u32 contrast, + s32 brightness) +{ + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + writel((((u32)brightness & 0xfful) << 8) | contrast, + &pregister->mlcvideolayer.mlcluenh); +} + +void nx_mlc_set_video_layer_chroma_enhance(u32 module_index, u32 quadrant, + s32 cb_a, s32 cb_b, + s32 cr_a, s32 cr_b) +{ + register struct nx_mlc_register_set *pregister; + register u32 temp; + + pregister = __g_module_variables[module_index].pregister; + temp = (((u32)cr_b & 0xfful) << 24) | (((u32)cr_a & 0xfful) << 16) | + (((u32)cb_b & 0xfful) << 8) | (((u32)cb_a & 0xfful) << 0); + if (quadrant > 0) { + writel(temp, &pregister->mlcvideolayer.mlcchenh[quadrant - 1]); + } else { + writel(temp, &pregister->mlcvideolayer.mlcchenh[0]); + writel(temp, &pregister->mlcvideolayer.mlcchenh[1]); + writel(temp, &pregister->mlcvideolayer.mlcchenh[2]); + writel(temp, &pregister->mlcvideolayer.mlcchenh[3]); + } +} + +void nx_mlc_set_video_layer_line_buffer_power_mode(u32 module_index, + int benable) +{ + const u32 linebuff_pwd_pos = 15; + const u32 linebuff_pwd_mask = 1ul << linebuff_pwd_pos; + const u32 dirtyflag_mask = 1ul << 4; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->mlcvideolayer.mlccontrol; + regvalue &= ~(linebuff_pwd_mask | dirtyflag_mask); + regvalue |= ((u32)benable << linebuff_pwd_pos); + + writel(regvalue, &pregister->mlcvideolayer.mlccontrol); +} + +int nx_mlc_get_video_layer_line_buffer_power_mode(u32 module_index) +{ + const u32 linebuff_pwd_pos = 15; + const u32 linebuff_pwd_mask = 1ul << linebuff_pwd_pos; + + return (int)((__g_module_variables[module_index] + .pregister->mlcvideolayer.mlccontrol & + linebuff_pwd_mask) >> linebuff_pwd_pos); +} + +void nx_mlc_set_video_layer_line_buffer_sleep_mode(u32 module_index, + int benable) +{ + const u32 linebuff_slmd_pos = 14; + const u32 linebuff_slmd_mask = 1ul << linebuff_slmd_pos; + const u32 dirtyflag_mask = 1ul << 4; + register u32 regvalue; + register struct nx_mlc_register_set *pregister; + + benable = (int)((u32)benable ^ 1); + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->mlcvideolayer.mlccontrol; + regvalue &= ~(linebuff_slmd_mask | dirtyflag_mask); + regvalue |= (benable << linebuff_slmd_pos); + + writel(regvalue, &pregister->mlcvideolayer.mlccontrol); +} + +int nx_mlc_get_video_layer_line_buffer_sleep_mode(u32 module_index) +{ + const u32 linebuff_slmd_pos = 14; + const u32 linebuff_slmd_mask = 1ul << linebuff_slmd_pos; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (linebuff_slmd_mask & pregister->mlcvideolayer.mlccontrol) + return 0; + else + return 1; +} + +void nx_mlc_set_video_layer_gama_table_power_mode(u32 module_index, int by, + int bu, int bv) +{ + const u32 vgammatable_pwd_bitpos = 17; + const u32 ugammatable_pwd_bitpos = 15; + const u32 ygammatable_pwd_bitpos = 13; + const u32 vgammatable_pwd_mask = (1 << vgammatable_pwd_bitpos); + const u32 ugammatable_pwd_mask = (1 << ugammatable_pwd_bitpos); + const u32 ygammatable_pwd_mask = (1 << ygammatable_pwd_bitpos); + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + read_value &= ~(ygammatable_pwd_mask | ugammatable_pwd_mask | + vgammatable_pwd_mask); + read_value |= (((u32)by << ygammatable_pwd_bitpos) | + ((u32)bu << ugammatable_pwd_bitpos) | + ((u32)bv << vgammatable_pwd_bitpos)); + + writel(read_value, &pregister->mlcgammacont); +} + +void nx_mlc_get_video_layer_gama_table_power_mode(u32 module_index, int *pby, + int *pbu, int *pbv) +{ + const u32 vgammatable_pwd_bitpos = 17; + const u32 ugammatable_pwd_bitpos = 15; + const u32 ygammatable_pwd_bitpos = 13; + const u32 vgammatable_pwd_mask = (1 << vgammatable_pwd_bitpos); + const u32 ugammatable_pwd_mask = (1 << ugammatable_pwd_bitpos); + const u32 ygammatable_pwd_mask = (1 << ygammatable_pwd_bitpos); + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + if (pby) + *pby = (read_value & ygammatable_pwd_mask) ? 1 : 0; + + if (pbu) + *pbu = (read_value & ugammatable_pwd_mask) ? 1 : 0; + + if (pbv) + *pbv = (read_value & vgammatable_pwd_mask) ? 1 : 0; +} + +void nx_mlc_set_video_layer_gama_table_sleep_mode(u32 module_index, int by, + int bu, int bv) +{ + const u32 vgammatable_sld_bitpos = 16; + const u32 ugammatable_sld_bitpos = 14; + const u32 ygammatable_sld_bitpos = 12; + const u32 vgammatable_sld_mask = (1 << vgammatable_sld_bitpos); + const u32 ugammatable_sld_mask = (1 << ugammatable_sld_bitpos); + const u32 ygammatable_sld_mask = (1 << ygammatable_sld_bitpos); + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + if (by) + read_value &= ~ygammatable_sld_mask; + else + read_value |= ygammatable_sld_mask; + + if (bu) + read_value &= ~ugammatable_sld_mask; + else + read_value |= ugammatable_sld_mask; + + if (bv) + read_value &= ~vgammatable_sld_mask; + else + read_value |= vgammatable_sld_mask; + + writel(read_value, &pregister->mlcgammacont); +} + +void nx_mlc_get_video_layer_gama_table_sleep_mode(u32 module_index, int *pby, + int *pbu, int *pbv) +{ + const u32 vgammatable_sld_bitpos = 16; + const u32 ugammatable_sld_bitpos = 14; + const u32 ygammatable_sld_bitpos = 12; + const u32 vgammatable_sld_mask = (1 << vgammatable_sld_bitpos); + const u32 ugammatable_sld_mask = (1 << ugammatable_sld_bitpos); + const u32 ygammatable_sld_mask = (1 << ygammatable_sld_bitpos); + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + + if (pby) + *pby = (read_value & vgammatable_sld_mask) ? 0 : 1; + + if (pbu) + *pbu = (read_value & ugammatable_sld_mask) ? 0 : 1; + + if (pbv) + *pbv = (read_value & ygammatable_sld_mask) ? 0 : 1; +} + +void nx_mlc_set_video_layer_gamma_enable(u32 module_index, int benable) +{ + const u32 yuvgammaemb_bitpos = 4; + const u32 yuvgammaemb_mask = 1 << yuvgammaemb_bitpos; + register u32 read_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->mlcgammacont; + read_value &= ~yuvgammaemb_mask; + read_value |= (u32)benable << yuvgammaemb_bitpos; + + writel(read_value, &pregister->mlcgammacont); +} + +int nx_mlc_get_video_layer_gamma_enable(u32 module_index) +{ + const u32 yuvgammaemb_bitpos = 4; + const u32 yuvgammaemb_mask = 1 << yuvgammaemb_bitpos; + + return (int)((__g_module_variables[module_index].pregister->mlcgammacont + & yuvgammaemb_mask) >> yuvgammaemb_bitpos); +} + +void nx_mlc_set_gamma_table_poweroff(u32 module_index, int enb) +{ + register struct nx_mlc_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + if (enb == 1) { + regvalue = pregister->mlcgammacont; + regvalue = regvalue & 0xf3; + writel(regvalue, &pregister->mlcgammacont); + } +} + +void nx_mlc_set_mlctop_control_parameter(u32 module_index, int field_enable, + int mlcenable, u8 priority, + enum g3daddrchangeallowed + g3daddr_change_allowed) +{ + register u32 mlctopcontrolreg; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + mlctopcontrolreg = (readl(&pregister->mlccontrolt)) & 0xfffffcfc; + mlctopcontrolreg = (u32)(mlctopcontrolreg | + ((priority << 8) | ((mlcenable == 1) << 1) | + (1 == + field_enable)) | (g3daddr_change_allowed << + 12)); + writel(mlctopcontrolreg, &pregister->mlccontrolt); +} + +void nx_mlc_set_rgb0layer_control_parameter(u32 module_index, int layer_enable, + int grp3denable, int tp_enable, + u32 transparency_color, + int inv_enable, u32 inverse_color, + int blend_enable, u8 alpha_value, + enum mlc_rgbfmt rbgformat, + enum locksizesel lock_size_select) +{ + u32 layer_format; + u32 control_enb; + u32 alpha_argument; + u32 lock_size = (u32)(lock_size_select & 0x3); + u32 rgb0controlreg; + u32 regvalue; + register struct nx_mlc_register_set *pregister; + + layer_format = nx_mlc_get_rgbformat(rbgformat); + pregister = __g_module_variables[module_index].pregister; + control_enb = + (u32)((grp3denable << 8) | (layer_enable << 5) | + (blend_enable << 2) | (inv_enable << 1) | tp_enable) & 0x127; + alpha_argument = (u32)(alpha_value & 0xf); + + rgb0controlreg = readl(&pregister->mlcrgblayer[0].mlccontrol) & 0x10; + regvalue = + (u32)(((layer_format << 16) | control_enb | (lock_size << 12)) | + rgb0controlreg); + writel(regvalue, &pregister->mlcrgblayer[0].mlccontrol); + + regvalue = (u32)((alpha_argument << 28) | transparency_color); + writel(regvalue, &pregister->mlcrgblayer[0].mlctpcolor); + regvalue = inverse_color; + writel(regvalue, &pregister->mlcrgblayer[0].mlcinvcolor); +} + +u32 nx_mlc_get_rgbformat(enum mlc_rgbfmt rbgformat) +{ + u32 rgbformatvalue; + const u32 format_table[] = { + 0x4432ul, 0x4342ul, 0x4211ul, 0x4120ul, 0x4003ul, 0x4554ul, + 0x3342ul, 0x2211ul, 0x1120ul, 0x1003ul, 0x4653ul, 0x4653ul, + 0x0653ul, 0x4ed3ul, 0x4f84ul, 0xc432ul, 0xc342ul, 0xc211ul, + 0xc120ul, 0xb342ul, 0xa211ul, 0x9120ul, 0xc653ul, 0xc653ul, + 0x8653ul, 0xced3ul, 0xcf84ul, 0x443aul + }; + + return rgbformatvalue = format_table[rbgformat]; +} + +void nx_mlc_set_rgb1layer_control_parameter(u32 module_index, int layer_enable, + int grp3denable, int tp_enable, + u32 transparency_color, + int inv_enable, u32 inverse_color, + int blend_enable, u8 alpha_value, + enum mlc_rgbfmt rbgformat, + enum locksizesel lock_size_select) +{ + u32 layer_format; + u32 control_enb; + u32 alpha_argument; + u32 lock_size = (u32)(lock_size_select & 0x3); + u32 rgb0controlreg; + u32 regvalue; + register struct nx_mlc_register_set *pregister; + + layer_format = nx_mlc_get_rgbformat(rbgformat); + pregister = __g_module_variables[module_index].pregister; + + rgb0controlreg = readl(&pregister->mlcrgblayer[1].mlccontrol) & 0x10; + control_enb = + (u32)((grp3denable << 8) | (layer_enable << 5) | + (blend_enable << 2) | (inv_enable << 1) | tp_enable) & 0x127; + alpha_argument = (u32)(alpha_value & 0xf); + regvalue = + (u32)(((layer_format << 16) | control_enb | (lock_size << 12)) | + rgb0controlreg); + writel(regvalue, &pregister->mlcrgblayer[1].mlccontrol); + regvalue = (u32)((alpha_argument << 28) | transparency_color); + writel(regvalue, &pregister->mlcrgblayer[1].mlctpcolor); + regvalue = inverse_color; + writel(regvalue, &pregister->mlcrgblayer[1].mlcinvcolor); +} + +void nx_mlc_set_rgb2layer_control_parameter(u32 module_index, int layer_enable, + int grp3denable, int tp_enable, + u32 transparency_color, + int inv_enable, u32 inverse_color, + int blend_enable, u8 alpha_value, + enum mlc_rgbfmt rbgformat, + enum locksizesel lock_size_select) +{ + u32 layer_format; + u32 control_enb; + u32 alpha_argument; + u32 lock_size = (u32)(lock_size_select & 0x3); + u32 rgb0controlreg; + u32 regvalue; + register struct nx_mlc_register_set *pregister; + + layer_format = nx_mlc_get_rgbformat(rbgformat); + pregister = __g_module_variables[module_index].pregister; + + rgb0controlreg = readl(&pregister->mlcrgblayer2.mlccontrol) & 0x10; + control_enb = + (u32)((grp3denable << 8) | (layer_enable << 5) | + (blend_enable << 2) | (inv_enable << 1) | tp_enable) & 0x127; + alpha_argument = (u32)(alpha_value & 0xf); + regvalue = + (u32)(((layer_format << 16) | control_enb | (lock_size << 12)) | + rgb0controlreg); + writel(regvalue, &pregister->mlcrgblayer2.mlccontrol); + regvalue = (u32)((alpha_argument << 28) | transparency_color); + writel(regvalue, &pregister->mlcrgblayer2.mlctpcolor); + regvalue = inverse_color; + writel(regvalue, &pregister->mlcrgblayer2.mlcinvcolor); +} + +void nx_mlc_set_video_layer_control_parameter(u32 module_index, + int layer_enable, int tp_enable, + u32 transparency_color, + int inv_enable, u32 inverse_color, + int blend_enable, u8 alpha_value, + enum nx_mlc_yuvfmt yuvformat) +{ + u32 control_enb; + u32 alpha_argument; + u32 regvalue; + register struct nx_mlc_register_set *pregister; + u32 video_control_reg; + + pregister = __g_module_variables[module_index].pregister; + + video_control_reg = readl(&pregister->mlcvideolayer.mlccontrol); + control_enb = + (u32)((yuvformat) | (layer_enable << 5) | (blend_enable << 2) | + (inv_enable << 1) | tp_enable) & 0x30027; + alpha_argument = (u32)(alpha_value & 0xf); + regvalue = (u32)(control_enb | video_control_reg); + writel(regvalue, &pregister->mlcvideolayer.mlccontrol); + regvalue = (u32)((alpha_argument << 28) | transparency_color); + writel(regvalue, &pregister->mlcvideolayer.mlctpcolor); + regvalue = (u32)((alpha_argument << 28) | transparency_color); + writel(regvalue, &pregister->mlcvideolayer.mlcinvcolor); +} + +void nx_mlc_set_srammode(u32 module_index, enum latyername layer_name, + enum srammode sram_mode) +{ + u32 control_reg_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + switch (layer_name) { + case topmlc: + control_reg_value = readl(&pregister->mlccontrolt); + writel((u32)(control_reg_value | (sram_mode << 10)), + &pregister->mlccontrolt); + control_reg_value = 0; + break; + case rgb0: + control_reg_value = + readl(&pregister->mlcrgblayer[0].mlccontrol); + writel((u32)(control_reg_value | (sram_mode << 14)), + &pregister->mlcrgblayer[0].mlccontrol); + control_reg_value = 0; + break; + case rgb1: + control_reg_value = + readl(&pregister->mlcrgblayer[1].mlccontrol); + writel((u32)(control_reg_value | (sram_mode << 14)), + &pregister->mlcrgblayer[1].mlccontrol); + control_reg_value = 0; + break; + case rgb2: + control_reg_value = readl(&pregister->mlcrgblayer2.mlccontrol); + writel((u32)(control_reg_value | (sram_mode << 14)), + &pregister->mlcrgblayer2.mlccontrol); + control_reg_value = 0; + break; + case video: + control_reg_value = readl(&pregister->mlcvideolayer.mlccontrol); + writel((u32)(control_reg_value | (sram_mode << 14)), + &pregister->mlcvideolayer.mlccontrol); + control_reg_value = 0; + break; + default: + break; + } +} + +void nx_mlc_set_layer_reg_finish(u32 module_index, enum latyername layer_name) +{ + u32 control_reg_value; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + switch (layer_name) { + case topmlc: + control_reg_value = readl(&pregister->mlccontrolt); + writel((u32)(control_reg_value | (1ul << 3)), + &pregister->mlccontrolt); + control_reg_value = 0; + break; + case rgb0: + control_reg_value = + readl(&pregister->mlcrgblayer[0].mlccontrol); + writel((u32)(control_reg_value | (1ul << 4)), + &pregister->mlcrgblayer[0].mlccontrol); + control_reg_value = 0; + break; + case rgb1: + control_reg_value = + readl(&pregister->mlcrgblayer[1].mlccontrol); + writel((u32)(control_reg_value | (1ul << 4)), + &pregister->mlcrgblayer[1].mlccontrol); + control_reg_value = 0; + break; + case rgb2: + control_reg_value = readl(&pregister->mlcrgblayer2.mlccontrol); + writel((u32)(control_reg_value | (1ul << 4)), + &pregister->mlcrgblayer2.mlccontrol); + control_reg_value = 0; + break; + case video: + control_reg_value = readl(&pregister->mlcvideolayer.mlccontrol); + writel((u32)(control_reg_value | (1ul << 4)), + &pregister->mlcvideolayer.mlccontrol); + control_reg_value = 0; + break; + default: + break; + } +} + +void nx_mlc_set_video_layer_coordinate(u32 module_index, int vfilterenable, + int hfilterenable, int vfilterenable_c, + int hfilterenable_c, + u16 video_layer_with, + u16 video_layer_height, s16 left, + s16 right, s16 top, + s16 bottom) +{ + s32 source_width, source_height; + s32 destination_width; + s32 destination_height; + s32 hscale, vscale; + s32 hfilterenb, vfilterenb; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((s32)(((left & 0x0fff) << 16) | (right & 0x0fff)), + &pregister->mlcvideolayer.mlcleftright); + writel((s32)(((top & 0x0fff) << 16) | (bottom & 0x0fff)), + &pregister->mlcvideolayer.mlctopbottom); + source_width = (s32)(video_layer_with - 1); + source_height = (s32)(video_layer_height - 1); + destination_width = (s32)(right - left); + destination_height = (s32)(bottom - top); + + hscale = + (s32)((source_width * (1ul << 11) + (destination_width / 2)) / + destination_width); + vscale = + (s32)((source_height * (1ul << 11) + + (destination_height / 2)) / destination_height); + + hfilterenb = (u32)(((hfilterenable_c << 29) | (hfilterenable) << 28)) & + 0x30000000; + vfilterenb = (u32)(((vfilterenable_c << 29) | (vfilterenable) << 28)) & + 0x30000000; + writel((u32)(hfilterenb | (hscale & 0x00ffffff)), + &pregister->mlcvideolayer.mlchscale); + writel((u32)(vfilterenb | (vscale & 0x00ffffff)), + &pregister->mlcvideolayer.mlcvscale); +} + +void nx_mlc_set_video_layer_filter_scale(u32 module_index, u32 hscale, + u32 vscale) +{ + register struct nx_mlc_register_set *pregister; + u32 mlchscale = 0; + u32 mlcvscale = 0; + + pregister = __g_module_variables[module_index].pregister; + mlchscale = readl(&pregister->mlcvideolayer.mlchscale) & (~0x00ffffff); + mlcvscale = readl(&pregister->mlcvideolayer.mlcvscale) & (~0x00ffffff); + + writel((u32)(mlchscale | (hscale & 0x00ffffff)), + &pregister->mlcvideolayer.mlchscale); + writel((u32)(mlcvscale | (vscale & 0x00ffffff)), + &pregister->mlcvideolayer.mlcvscale); +} + +void nx_mlc_set_gamma_control_parameter(u32 module_index, int rgbgammaenb, + int yuvgammaenb, int yuvalphaarray, + int dither_enb) +{ + u32 register_data; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + register_data = readl(&pregister->mlcgammacont); + register_data = (register_data & 0xf0c) | + ((yuvalphaarray << 5) | (yuvgammaenb << 4) | + (rgbgammaenb << 1) | (dither_enb << 0)); + writel(register_data, &pregister->mlcgammacont); +} + +void nx_mlc_set_layer_alpha256(u32 module_index, u32 layer, u32 alpha) +{ + u32 register_data; + register struct nx_mlc_register_set *pregister; + + if (alpha < 0) + alpha = 0; + if (alpha > 255) + alpha = 255; + + pregister = __g_module_variables[module_index].pregister; + if (layer == 0) { + register_data = + readl(&pregister->mlcrgblayer[0].mlctpcolor) & 0x00ffffff; + register_data = register_data | (alpha << 24); + writel(register_data, &pregister->mlcrgblayer[0].mlctpcolor); + } else if (layer == 1) { + register_data = + readl(&pregister->mlcrgblayer[1].mlctpcolor) & 0x00ffffff; + register_data = register_data | (alpha << 24); + writel(register_data, &pregister->mlcrgblayer[1].mlctpcolor); + } else if (layer == 2) { + register_data = + readl(&pregister->mlcrgblayer[1].mlctpcolor) & 0x00ffffff; + register_data = register_data | (alpha << 24); + writel(register_data, &pregister->mlcrgblayer2.mlctpcolor); + } else { + register_data = + readl(&pregister->mlcvideolayer.mlctpcolor) & 0x00ffffff; + register_data = register_data | (alpha << 24); + writel(register_data, &pregister->mlcvideolayer.mlctpcolor); + } +} + +int nx_mlc_is_under_flow(u32 module_index) +{ + const u32 underflow_pend_pos = 31; + const u32 underflow_pend_mask = 1ul << underflow_pend_pos; + + return (int)((__g_module_variables[module_index].pregister->mlccontrolt + & underflow_pend_mask) >> underflow_pend_pos); +} + +void nx_mlc_set_gamma_table(u32 module_index, int enb, + struct nx_mlc_gamma_table_parameter *p_gammatable) +{ + register struct nx_mlc_register_set *pregister; + u32 i, regval = 0; + + pregister = __g_module_variables[module_index].pregister; + if (enb == 1) { + regval = readl(&pregister->mlcgammacont); + + regval = (1 << 11) | (1 << 9) | (1 << 3); + writel(regval, &pregister->mlcgammacont); + + regval = regval | (1 << 10) | (1 << 8) | (1 << 2); + writel(regval, &pregister->mlcgammacont); + + for (i = 0; i < 256; i++) { + nx_mlc_set_rgblayer_rgamma_table(module_index, i, + p_gammatable->r_table[i]); + nx_mlc_set_rgblayer_ggamma_table(module_index, i, + p_gammatable->g_table[i]); + nx_mlc_set_rgblayer_bgamma_table(module_index, i, + p_gammatable->b_table[i]); + } + + regval = regval | (p_gammatable->alphaselect << 5) | + (p_gammatable->yuvgammaenb << 4 | + p_gammatable->allgammaenb << 4) | + (p_gammatable->rgbgammaenb << 1 | + p_gammatable->allgammaenb << 1) | + (p_gammatable->ditherenb << 1); + writel(regval, &pregister->mlcgammacont); + } else { + regval = regval & ~(1 << 10) & ~(1 << 8) & ~(1 << 2); + writel(regval, &pregister->mlcgammacont); + + regval = regval & ~(1 << 11) & ~(1 << 9) & ~(1 << 3); + writel(regval, &pregister->mlcgammacont); + } +} + +void nx_mlc_get_rgblayer_stride(u32 module_index, u32 layer, s32 *hstride, + s32 *vstride) +{ + unsigned int hs, vs; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + hs = readl(&pregister->mlcrgblayer[layer].mlchstride); + vs = readl(&pregister->mlcrgblayer[layer].mlcvstride); + + if (hstride) + *(s32 *)hstride = hs; + + if (vstride) + *(s32 *)vstride = vs; +} + +void nx_mlc_get_rgblayer_address(u32 module_index, u32 layer, + u32 *phys_address) +{ + u32 pa; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + pa = readl(&pregister->mlcrgblayer[layer].mlcaddress); + + if (phys_address) + *(u32 *)phys_address = pa; +} + +void nx_mlc_get_position(u32 module_index, u32 layer, int *left, int *top, + int *right, int *bottom) +{ + int lr, tb; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + lr = readl(&pregister->mlcrgblayer[layer].mlcleftright); + tb = readl(&pregister->mlcrgblayer[layer].mlctopbottom); + + if (left) + *(int *)left = ((lr >> 16) & 0xFFUL); + + if (top) + *(int *)top = ((tb >> 16) & 0xFFUL); + + if (right) + *(int *)right = ((lr >> 0) & 0xFFUL); + + if (bottom) + *(int *)bottom = ((tb >> 0) & 0xFFUL); +} + +void nx_mlc_get_video_layer_address_yuyv(u32 module_index, u32 *address, + u32 *stride) +{ + u32 a, s; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + a = readl(&pregister->mlcvideolayer.mlcaddress); + s = readl(&pregister->mlcvideolayer.mlcvstride); + + if (address) + *(u32 *)address = a; + + if (stride) + *(u32 *)stride = s; +} + +void nx_mlc_get_video_layer_address(u32 module_index, u32 *lu_address, + u32 *cb_address, u32 *cr_address) +{ + u32 lua, cba, cra; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + lua = readl(&pregister->mlcvideolayer.mlcaddress); + cba = readl(&pregister->mlcvideolayer.mlcaddresscb); + cra = readl(&pregister->mlcvideolayer.mlcaddresscr); + + if (lu_address) + *(u32 *)lu_address = lua; + + if (cb_address) + *(u32 *)cb_address = cba; + + if (cr_address) + *(u32 *)cr_address = cra; +} + +void nx_mlc_get_video_layer_stride(u32 module_index, u32 *lu_stride, + u32 *cb_stride, u32 *cr_stride) +{ + u32 lus, cbs, crs; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + lus = readl(&pregister->mlcvideolayer.mlcvstride); + cbs = readl(&pregister->mlcvideolayer.mlcvstridecb); + crs = readl(&pregister->mlcvideolayer.mlcvstridecr); + + if (lu_stride) + *(u32 *)lu_stride = lus; + + if (cb_stride) + *(u32 *)cb_stride = cbs; + + if (cr_stride) + *(u32 *)cr_stride = crs; +} + +void nx_mlc_get_video_position(u32 module_index, int *left, int *top, + int *right, int *bottom) +{ + int lr, tb; + register struct nx_mlc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + lr = readl(&pregister->mlcvideolayer.mlcleftright); + tb = readl(&pregister->mlcvideolayer.mlctopbottom); + + if (left) + *(int *)left = ((lr >> 16) & 0xFFUL); + + if (top) + *(int *)top = ((tb >> 16) & 0xFFUL); + + if (right) + *(int *)right = ((lr >> 0) & 0xFFUL); + + if (bottom) + *(int *)bottom = ((tb >> 0) & 0xFFUL); +} diff --git a/drivers/video/nexell/soc/s5pxx18_soc_mlc.h b/drivers/video/nexell/soc/s5pxx18_soc_mlc.h new file mode 100644 index 0000000..77ceca6 --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_mlc.h @@ -0,0 +1,429 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _S5PXX18_SOC_MLC_H_ +#define _S5PXX18_SOC_MLC_H_ + +#include "s5pxx18_soc_disptype.h" + +#define NUMBER_OF_MLC_MODULE 2 +#define PHY_BASEADDR_MLC0 0xC0102000 +#define PHY_BASEADDR_MLC1 0xC0102400 + +#define PHY_BASEADDR_MLC_LIST \ + { PHY_BASEADDR_MLC0, PHY_BASEADDR_MLC1 } + +struct nx_mlc_register_set { + u32 mlccontrolt; + u32 mlcscreensize; + u32 mlcbgcolor; + struct { + u32 mlcleftright; + u32 mlctopbottom; + u32 mlcinvalidleftright0; + u32 mlcinvalidtopbottom0; + u32 mlcinvalidleftright1; + u32 mlcinvalidtopbottom1; + u32 mlccontrol; + s32 mlchstride; + s32 mlcvstride; + u32 mlctpcolor; + u32 mlcinvcolor; + u32 mlcaddress; + u32 __reserved0; + } mlcrgblayer[2]; + struct { + u32 mlcleftright; + u32 mlctopbottom; + u32 mlccontrol; + u32 mlcvstride; + u32 mlctpcolor; + + u32 mlcinvcolor; + u32 mlcaddress; + u32 mlcaddresscb; + u32 mlcaddresscr; + s32 mlcvstridecb; + s32 mlcvstridecr; + u32 mlchscale; + u32 mlcvscale; + u32 mlcluenh; + u32 mlcchenh[4]; + } mlcvideolayer; + struct { + u32 mlcleftright; + u32 mlctopbottom; + u32 mlcinvalidleftright0; + u32 mlcinvalidtopbottom0; + u32 mlcinvalidleftright1; + u32 mlcinvalidtopbottom1; + u32 mlccontrol; + s32 mlchstride; + s32 mlcvstride; + u32 mlctpcolor; + u32 mlcinvcolor; + u32 mlcaddress; + } mlcrgblayer2; + u32 mlcpaletetable2; + u32 mlcgammacont; + u32 mlcrgammatablewrite; + u32 mlcggammatablewrite; + u32 mlcbgammatablewrite; + u32 yuvlayergammatable_red; + u32 yuvlayergammatable_green; + u32 yuvlayergammatable_blue; + + u32 dimctrl; + u32 dimlut0; + u32 dimlut1; + u32 dimbusyflag; + u32 dimprdarrr0; + u32 dimprdarrr1; + u32 dimram0rddata; + u32 dimram1rddata; + u32 __reserved2[(0x3c0 - 0x12c) / 4]; + u32 mlcclkenb; +}; + +enum nx_mlc_priority { + nx_mlc_priority_videofirst = 0ul, + nx_mlc_priority_videosecond = 1ul, + nx_mlc_priority_videothird = 2ul, + nx_mlc_priority_videofourth = 3ul +}; + +enum nx_mlc_rgbfmt { + nx_mlc_rgbfmt_r5g6b5 = 0x44320000ul, + nx_mlc_rgbfmt_b5g6r5 = 0xc4320000ul, + nx_mlc_rgbfmt_x1r5g5b5 = 0x43420000ul, + nx_mlc_rgbfmt_x1b5g5r5 = 0xc3420000ul, + nx_mlc_rgbfmt_x4r4g4b4 = 0x42110000ul, + nx_mlc_rgbfmt_x4b4g4r4 = 0xc2110000ul, + nx_mlc_rgbfmt_x8r3g3b2 = 0x41200000ul, + nx_mlc_rgbfmt_x8b3g3r2 = 0xc1200000ul, + nx_mlc_rgbfmt_a1r5g5b5 = 0x33420000ul, + nx_mlc_rgbfmt_a1b5g5r5 = 0xb3420000ul, + nx_mlc_rgbfmt_a4r4g4b4 = 0x22110000ul, + nx_mlc_rgbfmt_a4b4g4r4 = 0xa2110000ul, + nx_mlc_rgbfmt_a8r3g3b2 = 0x11200000ul, + nx_mlc_rgbfmt_a8b3g3r2 = 0x91200000ul, + nx_mlc_rgbfmt_r8g8b8 = 0x46530000ul, + nx_mlc_rgbfmt_b8g8r8 = 0xc6530000ul, + nx_mlc_rgbfmt_x8r8g8b8 = 0x46530000ul, + nx_mlc_rgbfmt_x8b8g8r8 = 0xc6530000ul, + nx_mlc_rgbfmt_a8r8g8b8 = 0x06530000ul, + nx_mlc_rgbfmt_a8b8g8r8 = 0x86530000ul +}; + +enum nx_mlc_yuvfmt { + nx_mlc_yuvfmt_420 = 0ul << 16, + nx_mlc_yuvfmt_422 = 1ul << 16, + nx_mlc_yuvfmt_444 = 3ul << 16, + nx_mlc_yuvfmt_yuyv = 2ul << 16, + nx_mlc_yuvfmt_422_cbcr = 4ul << 16, + nx_mlc_yuvfmt_420_cbcr = 5ul << 16, +}; + +#ifdef __arm +#pragma diag_default 66 +#endif + +int nx_mlc_initialize(void); +u32 nx_mlc_get_number_of_module(void); +u32 nx_mlc_get_physical_address(u32 module_index); +u32 nx_mlc_get_size_of_register_set(void); +void nx_mlc_set_base_address(u32 module_index, void *base_address); +void *nx_mlc_get_base_address(u32 module_index); +int nx_mlc_open_module(u32 module_index); +int nx_mlc_close_module(u32 module_index); +int nx_mlc_check_busy(u32 module_index); +int nx_mlc_can_power_down(u32 module_index); +void nx_mlc_set_clock_pclk_mode(u32 module_index, enum nx_pclkmode mode); +enum nx_pclkmode nx_mlc_get_clock_pclk_mode(u32 module_index); +void nx_mlc_set_clock_bclk_mode(u32 module_index, enum nx_bclkmode mode); +enum nx_bclkmode nx_mlc_get_clock_bclk_mode(u32 module_index); + +void nx_mlc_set_top_power_mode(u32 module_index, int bpower); +int nx_mlc_get_top_power_mode(u32 module_index); +void nx_mlc_set_top_sleep_mode(u32 module_index, int bsleep); +int nx_mlc_get_top_sleep_mode(u32 module_index); +void nx_mlc_set_top_dirty_flag(u32 module_index); +int nx_mlc_get_top_dirty_flag(u32 module_index); +void nx_mlc_set_mlc_enable(u32 module_index, int benb); +int nx_mlc_get_mlc_enable(u32 module_index); +void nx_mlc_set_field_enable(u32 module_index, int benb); +int nx_mlc_get_field_enable(u32 module_index); +void nx_mlc_set_layer_priority(u32 module_index, + enum nx_mlc_priority priority); +void nx_mlc_set_screen_size(u32 module_index, u32 width, u32 height); +void nx_mlc_get_screen_size(u32 module_index, u32 *pwidth, + u32 *pheight); +void nx_mlc_set_background(u32 module_index, u32 color); + +void nx_mlc_set_dirty_flag(u32 module_index, u32 layer); +int nx_mlc_get_dirty_flag(u32 module_index, u32 layer); +void nx_mlc_set_layer_enable(u32 module_index, u32 layer, int benb); +int nx_mlc_get_layer_enable(u32 module_index, u32 layer); +void nx_mlc_set_lock_size(u32 module_index, u32 layer, u32 locksize); +void nx_mlc_set_alpha_blending(u32 module_index, u32 layer, int benb, + u32 alpha); +void nx_mlc_set_transparency(u32 module_index, u32 layer, int benb, + u32 color); +void nx_mlc_set_color_inversion(u32 module_index, u32 layer, int benb, + u32 color); +u32 nx_mlc_get_extended_color(u32 module_index, u32 color, + enum nx_mlc_rgbfmt format); +void nx_mlc_set_format_rgb(u32 module_index, u32 layer, + enum nx_mlc_rgbfmt format); +void nx_mlc_set_format_yuv(u32 module_index, enum nx_mlc_yuvfmt format); +void nx_mlc_set_position(u32 module_index, u32 layer, s32 sx, + s32 sy, s32 ex, s32 ey); +void nx_mlc_set_dither_enable_when_using_gamma(u32 module_index, + int benable); +int nx_mlc_get_dither_enable_when_using_gamma(u32 module_index); +void nx_mlc_set_gamma_priority(u32 module_index, int bvideolayer); +int nx_mlc_get_gamma_priority(u32 module_index); + +void nx_mlc_set_rgblayer_invalid_position(u32 module_index, u32 layer, + u32 region, s32 sx, + s32 sy, s32 ex, + s32 ey, int benb); +void nx_mlc_set_rgblayer_stride(u32 module_index, u32 layer, + s32 hstride, s32 vstride); +void nx_mlc_set_rgblayer_address(u32 module_index, u32 layer, u32 addr); +void nx_mlc_set_rgblayer_gama_table_power_mode(u32 module_index, + int bred, int bgreen, + int bblue); +void nx_mlc_get_rgblayer_gama_table_power_mode(u32 module_index, + int *pbred, int *pbgreen, + int *pbblue); +void nx_mlc_set_rgblayer_gama_table_sleep_mode(u32 module_index, + int bred, int bgreen, + int bblue); +void nx_mlc_get_rgblayer_gama_table_sleep_mode(u32 module_index, + int *pbred, int *pbgreen, + int *pbblue); +void nx_mlc_set_rgblayer_rgamma_table(u32 module_index, u32 dwaddress, + u32 dwdata); +void nx_mlc_set_rgblayer_ggamma_table(u32 module_index, u32 dwaddress, + u32 dwdata); +void nx_mlc_set_rgblayer_bgamma_table(u32 module_index, u32 dwaddress, + u32 dwdata); +void nx_mlc_set_rgblayer_gamma_enable(u32 module_index, int benable); +int nx_mlc_get_rgblayer_gamma_enable(u32 module_index); + +void nx_mlc_set_video_layer_stride(u32 module_index, s32 lu_stride, + s32 cb_stride, s32 cr_stride); +void nx_mlc_set_video_layer_address(u32 module_index, u32 lu_addr, + u32 cb_addr, u32 cr_addr); +void nx_mlc_set_video_layer_address_yuyv(u32 module_index, u32 addr, + s32 stride); +void nx_mlc_set_video_layer_scale_factor(u32 module_index, u32 hscale, + u32 vscale, int bhlumaenb, + int bhchromaenb, int bvlumaenb, + int bvchromaenb); +void nx_mlc_set_video_layer_scale_filter(u32 module_index, int bhlumaenb, + int bhchromaenb, int bvlumaenb, + int bvchromaenb); +void nx_mlc_get_video_layer_scale_filter(u32 module_index, + int *bhlumaenb, + int *bhchromaenb, + int *bvlumaenb, + int *bvchromaenb); +void nx_mlc_set_video_layer_scale(u32 module_index, u32 sw, u32 sh, + u32 dw, u32 dh, int bhlumaenb, + int bhchromaenb, int bvlumaenb, + int bvchromaenb); +void nx_mlc_set_video_layer_luma_enhance(u32 module_index, u32 contrast, + s32 brightness); +void nx_mlc_set_video_layer_chroma_enhance(u32 module_index, + u32 quadrant, s32 cb_a, + s32 cb_b, s32 cr_a, + s32 cr_b); +void nx_mlc_set_video_layer_line_buffer_power_mode(u32 module_index, + int benable); +int nx_mlc_get_video_layer_line_buffer_power_mode(u32 module_index); +void nx_mlc_set_video_layer_line_buffer_sleep_mode(u32 module_index, + int benable); +int nx_mlc_get_video_layer_line_buffer_sleep_mode(u32 module_index); +void nx_mlc_set_video_layer_gamma_enable(u32 module_index, int benable); +int nx_mlc_get_video_layer_gamma_enable(u32 module_index); + +void nx_mlc_set_gamma_table_poweroff(u32 module_index, int enb); + +enum mlc_rgbfmt { + rgbfmt_r5g6b5 = 0, + rgbfmt_x1r5g5b5 = 1, + rgbfmt_x4r4g4b4 = 2, + rgbfmt_x8r3g3b2 = 3, + rgbfmt_x8l8 = 4, + rgbfmt_l16 = 5, + rgbfmt_a1r5g5b5 = 6, + rgbfmt_a4r4g4b4 = 7, + rgbfmt_a8r3g3b2 = 8, + rgbfmt_a8l8 = 9, + rgbfmt_r8g8b8 = 10, + rgbfmt_x8r8g8b8 = 11, + rgbfmt_a8r8g8b8 = 12, + rgbfmt_g8r8_g8b8 = 13, + rgbfmt_r8g8_b8g8 = 14, + rgbfmt_b5g6r5 = 15, + rgbfmt_x1b5g5r5 = 16, + rgbfmt_x4b4g4r4 = 17, + rgbfmt_x8b3g3r2 = 18, + rgbfmt_a1b5g5r5 = 19, + rgbfmt_a4b4g4r4 = 20, + rgbfmt_a8b3g3r2 = 21, + rgbfmt_b8g8r8 = 22, + rgbfmt_x8b8g8r8 = 23, + rgbfmt_a8b8g8r8 = 24, + rgbfmt_g8b8_g8r8 = 25, + rgbfmt_b8g8_r8g8 = 26, + rgbfmt_pataletb = 27 +}; + +enum latyername { + topmlc = 0, + rgb0 = 1, + rgb1 = 2, + rgb2 = 3, + video = 4 +}; + +enum srammode { + poweroff = 0, + sleepmode = 2, + run = 3 +}; + +enum locksizesel { + locksize_4 = 0, + locksize_8 = 1, + locksize_16 = 2 +}; + +enum g3daddrchangeallowed { + prim = 0, + secon = 1, + primorsecon = 2, + primandsecon = 3 +}; + +void nx_mlc_set_mlctop_control_parameter(u32 module_index, + int field_enable, int mlcenable, + u8 priority, + enum g3daddrchangeallowed + g3daddr_change_allowed); +void nx_mlc_set_rgb0layer_control_parameter(u32 module_index, + int layer_enable, + int grp3denable, + int tp_enable, + u32 transparency_color, + int inv_enable, + u32 inverse_color, + int blend_enable, + u8 alpha_value, + enum mlc_rgbfmt rbgformat, + enum locksizesel + lock_size_select); + +u32 nx_mlc_get_rgbformat(enum mlc_rgbfmt rbgformat); +void nx_mlc_set_rgb1layer_control_parameter(u32 module_index, + int layer_enable, + int grp3denable, + int tp_enable, + u32 transparency_color, + int inv_enable, + u32 inverse_color, + int blend_enable, + u8 alpha_value, + enum mlc_rgbfmt rbgformat, + enum locksizesel + lock_size_select); + +void nx_mlc_set_rgb2layer_control_parameter(u32 module_index, + int layer_enable, + int grp3denable, + int tp_enable, + u32 transparency_color, + int inv_enable, + u32 inverse_color, + int blend_enable, + u8 alpha_value, + enum mlc_rgbfmt rbgformat, + enum locksizesel + lock_size_select); + +void nx_mlc_set_video_layer_control_parameter(u32 module_index, + int layer_enable, + int tp_enable, + u32 transparency_color, + int inv_enable, + u32 inverse_color, + int blend_enable, + u8 alpha_value, + enum nx_mlc_yuvfmt + yuvformat); + +void nx_mlc_set_srammode(u32 module_index, enum latyername layer_name, + enum srammode sram_mode); + +void nx_mlc_set_layer_reg_finish(u32 module_index, + enum latyername layer_name); + +void nx_mlc_set_video_layer_coordinate(u32 module_index, + int vfilterenable, + int hfilterenable, + int vfilterenable_c, + int hfilterenable_c, + u16 video_layer_with, + u16 video_layer_height, + s16 left, s16 right, + s16 top, s16 bottom); + +void nx_mlc_set_video_layer_filter_scale(u32 module_index, u32 hscale, + u32 vscale); +void nx_mlcsetgammasrammode(u32 module_index, enum srammode sram_mode); +void nx_mlc_set_gamma_control_parameter(u32 module_index, + int rgbgammaenb, int yuvgammaenb, + int yuvalphaarray, + int dither_enb); + +void nx_mlc_set_layer_alpha256(u32 module_index, u32 layer, u32 alpha); +int nx_mlc_is_under_flow(u32 module_index); + +struct nx_mlc_gamma_table_parameter { + u32 r_table[256]; + u32 g_table[256]; + u32 b_table[256]; + u32 ditherenb; + u32 alphaselect; + u32 yuvgammaenb; + u32 rgbgammaenb; + u32 allgammaenb; +}; + +void nx_mlc_set_gamma_table(u32 module_index, int enb, + struct nx_mlc_gamma_table_parameter *p_gammatable); +void nx_mlc_get_rgblayer_stride(u32 module_index, u32 layer, + s32 *hstride, s32 *vstride); +void nx_mlc_get_rgblayer_address(u32 module_index, u32 layer, + u32 *phys_address); +void nx_mlc_get_position(u32 module_index, u32 layer, int *left, + int *top, int *right, int *bottom); +void nx_mlc_get_video_layer_address_yuyv(u32 module_index, u32 *address, + u32 *stride); +void nx_mlc_get_video_layer_address(u32 module_index, u32 *lu_address, + u32 *cb_address, u32 *cr_address); +void nx_mlc_get_video_layer_stride(u32 module_index, u32 *lu_stride, + u32 *cb_stride, u32 *cr_stride); +void nx_mlc_get_video_layer_stride(u32 module_index, u32 *lu_stride, + u32 *cb_stride, u32 *cr_stride); +void nx_mlc_get_video_position(u32 module_index, int *left, int *top, + int *right, int *bottom); + +#endif

Low level functions for LVDS and HDMI display interfaces.
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
drivers/video/nexell/soc/s5pxx18_soc_hdmi.c | 50 +++ drivers/video/nexell/soc/s5pxx18_soc_hdmi.h | 488 ++++++++++++++++++++++++++++ drivers/video/nexell/soc/s5pxx18_soc_lvds.c | 278 ++++++++++++++++ drivers/video/nexell/soc/s5pxx18_soc_lvds.h | 83 +++++ 4 files changed, 899 insertions(+) create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_hdmi.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_hdmi.h create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_lvds.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_lvds.h
diff --git a/drivers/video/nexell/soc/s5pxx18_soc_hdmi.c b/drivers/video/nexell/soc/s5pxx18_soc_hdmi.c new file mode 100644 index 0000000..7b8be7e --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_hdmi.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <linux/types.h> +#include <linux/io.h> + +#include "s5pxx18_soc_hdmi.h" + +static u32 *hdmi_base_addr; + +u32 nx_hdmi_get_reg(u32 module_index, u32 offset) +{ + u32 *reg_addr; + u32 regvalue; + + reg_addr = hdmi_base_addr + (offset / sizeof(u32)); + regvalue = readl((u32 *)reg_addr); + + return regvalue; +} + +void nx_hdmi_set_reg(u32 module_index, u32 offset, u32 regvalue) +{ + s64 offset_new = (s64)((int32_t)offset); + u32 *reg_addr; + + reg_addr = hdmi_base_addr + (offset_new / sizeof(u32)); + writel(regvalue, (u32 *)reg_addr); +} + +void nx_hdmi_set_base_address(u32 module_index, void *base_address) +{ + hdmi_base_addr = (u32 *)base_address; +} + +void *nx_hdmi_get_base_address(u32 module_index) +{ + return (u32 *)hdmi_base_addr; +} + +u32 nx_hdmi_get_physical_address(u32 module_index) +{ + const u32 physical_addr[] = PHY_BASEADDR_HDMI_LIST; + + return physical_addr[module_index]; +} diff --git a/drivers/video/nexell/soc/s5pxx18_soc_hdmi.h b/drivers/video/nexell/soc/s5pxx18_soc_hdmi.h new file mode 100644 index 0000000..a4c5ab5 --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_hdmi.h @@ -0,0 +1,488 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _S5PXX18_SOC_HDMI_H_ +#define _S5PXX18_SOC_HDMI_H_ + +#include "s5pxx18_soc_disptop.h" + +#define PHY_BASEADDR_HDMI_PHY_MODULE 0xc00f0000 +#define PHY_BASEADDR_HDMI_LIST \ + { PHY_BASEADDR_HDMI_MODULE } + +#define HDMI_LINK_INTC_CON_0 (HDMI_ADDR_OFFSET + 0x00000000) +#define HDMI_LINK_INTC_FLAG_0 (HDMI_ADDR_OFFSET + 0x00000004) +#define HDMI_LINK_AESKEY_VALID (HDMI_ADDR_OFFSET + 0x00000008) +#define HDMI_LINK_HPD (HDMI_ADDR_OFFSET + 0x0000000C) +#define HDMI_LINK_INTC_CON_1 (HDMI_ADDR_OFFSET + 0x00000010) +#define HDMI_LINK_INTC_FLAG_1 (HDMI_ADDR_OFFSET + 0x00000014) +#define HDMI_LINK_PHY_STATUS_0 (HDMI_ADDR_OFFSET + 0x00000020) +#define HDMI_LINK_PHY_STATUS_CMU (HDMI_ADDR_OFFSET + 0x00000024) +#define HDMI_LINK_PHY_STATUS_PLL (HDMI_ADDR_OFFSET + 0x00000028) +#define HDMI_LINK_PHY_CON_0 (HDMI_ADDR_OFFSET + 0x00000030) +#define HDMI_LINK_HPD_CTRL (HDMI_ADDR_OFFSET + 0x00000040) +#define HDMI_LINK_HPD_STATUS (HDMI_ADDR_OFFSET + 0x00000044) +#define HDMI_LINK_HPD_TH_x (HDMI_ADDR_OFFSET + 0x00000050) + +#define HDMI_LINK_HDMI_CON_0 (HDMI_ADDR_OFFSET + 0x00010000) +#define HDMI_LINK_HDMI_CON_1 (HDMI_ADDR_OFFSET + 0x00010004) +#define HDMI_LINK_HDMI_CON_2 (HDMI_ADDR_OFFSET + 0x00010008) +#define HDMI_LINK_STATUS (HDMI_ADDR_OFFSET + 0x00010010) +#define HDMI_LINK_STATUS_EN (HDMI_ADDR_OFFSET + 0x00010020) + +#define HDMI_LINK_HDCP_SHA1_REN0 (HDMI_ADDR_OFFSET + 0x00010024) +#define HDMI_LINK_HDCP_SHA1_REN1 (HDMI_ADDR_OFFSET + 0x00010028) + +#define HDMI_LINK_MODE_SEL (HDMI_ADDR_OFFSET + 0x00010040) +#define HDMI_LINK_ENC_EN (HDMI_ADDR_OFFSET + 0x00010044) +#define HDMI_LINK_HDMI_YMAX (HDMI_ADDR_OFFSET + 0x00010060) +#define HDMI_LINK_HDMI_YMIN (HDMI_ADDR_OFFSET + 0x00010064) +#define HDMI_LINK_HDMI_CMAX (HDMI_ADDR_OFFSET + 0x00010068) +#define HDMI_LINK_HDMI_CMIN (HDMI_ADDR_OFFSET + 0x0001006C) +#define HDMI_LINK_H_BLANK_0 (HDMI_ADDR_OFFSET + 0x000100A0) +#define HDMI_LINK_H_BLANK_1 (HDMI_ADDR_OFFSET + 0x000100A4) +#define HDMI_LINK_V2_BLANK_0 (HDMI_ADDR_OFFSET + 0x000100B0) +#define HDMI_LINK_V2_BLANK_1 (HDMI_ADDR_OFFSET + 0x000100B4) +#define HDMI_LINK_V1_BLANK_0 (HDMI_ADDR_OFFSET + 0x000100B8) +#define HDMI_LINK_V1_BLANK_1 (HDMI_ADDR_OFFSET + 0x000100BC) +#define HDMI_LINK_V_LINE_0 (HDMI_ADDR_OFFSET + 0x000100C0) +#define HDMI_LINK_V_LINE_1 (HDMI_ADDR_OFFSET + 0x000100C4) +#define HDMI_LINK_H_LINE_0 (HDMI_ADDR_OFFSET + 0x000100C8) +#define HDMI_LINK_H_LINE_1 (HDMI_ADDR_OFFSET + 0x000100CC) +#define HDMI_LINK_HSYNC_POL (HDMI_ADDR_OFFSET + 0x000100E0) +#define HDMI_LINK_VSYNC_POL (HDMI_ADDR_OFFSET + 0x000100E4) +#define HDMI_LINK_INT_PRO_MODE (HDMI_ADDR_OFFSET + 0x000100E8) +#define HDMI_LINK_SEND_START_0 (HDMI_ADDR_OFFSET + 0x000100F0) +#define HDMI_LINK_SEND_START_1 (HDMI_ADDR_OFFSET + 0x000100F4) +#define HDMI_LINK_SEND_END_0 (HDMI_ADDR_OFFSET + 0x00010100) +#define HDMI_LINK_SEND_END_1 (HDMI_ADDR_OFFSET + 0x00010104) +#define HDMI_LINK_SEND_END_2 (HDMI_ADDR_OFFSET + 0x00010108) +#define HDMI_LINK_V_BLANK_F0_0 (HDMI_ADDR_OFFSET + 0x00010110) +#define HDMI_LINK_V_BLANK_F0_1 (HDMI_ADDR_OFFSET + 0x00010114) +#define HDMI_LINK_V_BLANK_F1_0 (HDMI_ADDR_OFFSET + 0x00010118) +#define HDMI_LINK_V_BLANK_F1_1 (HDMI_ADDR_OFFSET + 0x0001011C) +#define HDMI_LINK_H_SYNC_START_0 (HDMI_ADDR_OFFSET + 0x00010120) +#define HDMI_LINK_H_SYNC_START_1 (HDMI_ADDR_OFFSET + 0x00010124) +#define HDMI_LINK_H_SYNC_END_0 (HDMI_ADDR_OFFSET + 0x00010128) +#define HDMI_LINK_H_SYNC_END_1 (HDMI_ADDR_OFFSET + 0x0001012C) +#define HDMI_LINK_V_SYNC_LINE_BEF_2_0 (HDMI_ADDR_OFFSET + 0x00010130) +#define HDMI_LINK_V_SYNC_LINE_BEF_2_1 (HDMI_ADDR_OFFSET + 0x00010134) +#define HDMI_LINK_V_SYNC_LINE_BEF_1_0 (HDMI_ADDR_OFFSET + 0x00010138) +#define HDMI_LINK_V_SYNC_LINE_BEF_1_1 (HDMI_ADDR_OFFSET + 0x0001013C) +#define HDMI_LINK_V_SYNC_LINE_AFT_2_0 (HDMI_ADDR_OFFSET + 0x00010140) +#define HDMI_LINK_V_SYNC_LINE_AFT_2_1 (HDMI_ADDR_OFFSET + 0x00010144) +#define HDMI_LINK_V_SYNC_LINE_AFT_1_0 (HDMI_ADDR_OFFSET + 0x00010148) +#define HDMI_LINK_V_SYNC_LINE_AFT_1_1 (HDMI_ADDR_OFFSET + 0x0001014C) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_0 (HDMI_ADDR_OFFSET + 0x00010150) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_1 (HDMI_ADDR_OFFSET + 0x00010154) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_0 (HDMI_ADDR_OFFSET + 0x00010158) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_1 (HDMI_ADDR_OFFSET + 0x0001015C) +#define HDMI_LINK_V_BLANK_F2_0 (HDMI_ADDR_OFFSET + 0x00010160) +#define HDMI_LINK_V_BLANK_F2_1 (HDMI_ADDR_OFFSET + 0x00010164) +#define HDMI_LINK_V_BLANK_F3_0 (HDMI_ADDR_OFFSET + 0x00010168) +#define HDMI_LINK_V_BLANK_F3_1 (HDMI_ADDR_OFFSET + 0x0001016C) +#define HDMI_LINK_V_BLANK_F4_0 (HDMI_ADDR_OFFSET + 0x00010170) +#define HDMI_LINK_V_BLANK_F4_1 (HDMI_ADDR_OFFSET + 0x00010174) +#define HDMI_LINK_V_BLANK_F5_0 (HDMI_ADDR_OFFSET + 0x00010178) +#define HDMI_LINK_V_BLANK_F5_1 (HDMI_ADDR_OFFSET + 0x0001017C) +#define HDMI_LINK_V_SYNC_LINE_AFT_3_0 (HDMI_ADDR_OFFSET + 0x00010180) +#define HDMI_LINK_V_SYNC_LINE_AFT_3_1 (HDMI_ADDR_OFFSET + 0x00010184) +#define HDMI_LINK_V_SYNC_LINE_AFT_4_0 (HDMI_ADDR_OFFSET + 0x00010188) +#define HDMI_LINK_V_SYNC_LINE_AFT_4_1 (HDMI_ADDR_OFFSET + 0x0001018C) +#define HDMI_LINK_V_SYNC_LINE_AFT_5_0 (HDMI_ADDR_OFFSET + 0x00010190) +#define HDMI_LINK_V_SYNC_LINE_AFT_5_1 (HDMI_ADDR_OFFSET + 0x00010194) +#define HDMI_LINK_V_SYNC_LINE_AFT_6_0 (HDMI_ADDR_OFFSET + 0x00010198) +#define HDMI_LINK_V_SYNC_LINE_AFT_6_1 (HDMI_ADDR_OFFSET + 0x0001019C) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_0 (HDMI_ADDR_OFFSET + 0x000101A0) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_1 (HDMI_ADDR_OFFSET + 0x000101A4) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_0 (HDMI_ADDR_OFFSET + 0x000101A8) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_1 (HDMI_ADDR_OFFSET + 0x000101AC) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_0 (HDMI_ADDR_OFFSET + 0x000101B0) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_1 (HDMI_ADDR_OFFSET + 0x000101B4) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_0 (HDMI_ADDR_OFFSET + 0x000101B8) +#define HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_1 (HDMI_ADDR_OFFSET + 0x000101BC) +#define HDMI_LINK_VACT_SPACE1_0 (HDMI_ADDR_OFFSET + 0x000101C0) +#define HDMI_LINK_VACT_SPACE1_1 (HDMI_ADDR_OFFSET + 0x000101C4) +#define HDMI_LINK_VACT_SPACE2_0 (HDMI_ADDR_OFFSET + 0x000101C8) +#define HDMI_LINK_VACT_SPACE2_1 (HDMI_ADDR_OFFSET + 0x000101CC) +#define HDMI_LINK_VACT_SPACE3_0 (HDMI_ADDR_OFFSET + 0x000101D0) +#define HDMI_LINK_VACT_SPACE3_1 (HDMI_ADDR_OFFSET + 0x000101D4) +#define HDMI_LINK_VACT_SPACE4_0 (HDMI_ADDR_OFFSET + 0x000101D8) +#define HDMI_LINK_VACT_SPACE4_1 (HDMI_ADDR_OFFSET + 0x000101DC) +#define HDMI_LINK_VACT_SPACE5_0 (HDMI_ADDR_OFFSET + 0x000101E0) +#define HDMI_LINK_VACT_SPACE5_1 (HDMI_ADDR_OFFSET + 0x000101E4) +#define HDMI_LINK_VACT_SPACE6_0 (HDMI_ADDR_OFFSET + 0x000101E8) +#define HDMI_LINK_VACT_SPACE6_1 (HDMI_ADDR_OFFSET + 0x000101EC) + +#define HDMI_LINK_CSC_MUX (HDMI_ADDR_OFFSET + 0x000101F0) +#define HDMI_LINK_SYNC_GEN_MUX (HDMI_ADDR_OFFSET + 0x000101F4) + +#define HDMI_LINK_GCP_CON (HDMI_ADDR_OFFSET + 0x00010200) +#define HDMI_LINK_GCP_BYTE1 (HDMI_ADDR_OFFSET + 0x00010210) +#define HDMI_LINK_GCP_BYTE2 (HDMI_ADDR_OFFSET + 0x00010214) +#define HDMI_LINK_GCP_BYTE3 (HDMI_ADDR_OFFSET + 0x00010218) +#define HDMI_LINK_ASP_CON (HDMI_ADDR_OFFSET + 0x00010300) +#define HDMI_LINK_ASP_SP_FLAT (HDMI_ADDR_OFFSET + 0x00010304) +#define HDMI_LINK_ASP_CHCFG0 (HDMI_ADDR_OFFSET + 0x00010310) +#define HDMI_LINK_ASP_CHCFG1 (HDMI_ADDR_OFFSET + 0x00010314) +#define HDMI_LINK_ASP_CHCFG2 (HDMI_ADDR_OFFSET + 0x00010318) +#define HDMI_LINK_ASP_CHCFG3 (HDMI_ADDR_OFFSET + 0x0001031C) +#define HDMI_LINK_ACR_CON (HDMI_ADDR_OFFSET + 0x00010400) +#define HDMI_LINK_ACR_MCTS0 (HDMI_ADDR_OFFSET + 0x00010410) +#define HDMI_LINK_ACR_MCTS1 (HDMI_ADDR_OFFSET + 0x00010414) +#define HDMI_LINK_ACR_MCTS2 (HDMI_ADDR_OFFSET + 0x00010418) +#define HDMI_LINK_ACR_N0 (HDMI_ADDR_OFFSET + 0x00010430) +#define HDMI_LINK_ACR_N1 (HDMI_ADDR_OFFSET + 0x00010434) +#define HDMI_LINK_ACR_N2 (HDMI_ADDR_OFFSET + 0x00010438) +#define HDMI_LINK_ACP_CON (HDMI_ADDR_OFFSET + 0x00010500) +#define HDMI_LINK_ACP_TYPE (HDMI_ADDR_OFFSET + 0x00010514) +#define HDMI_LINK_ACP_DATAX (HDMI_ADDR_OFFSET + 0x00010520) +#define HDMI_LINK_ISRC_CON (HDMI_ADDR_OFFSET + 0x00010600) +#define HDMI_LINK_ISRC1_HEADER1 (HDMI_ADDR_OFFSET + 0x00010614) +#define HDMI_LINK_ISRC1_DATAX (HDMI_ADDR_OFFSET + 0x00010620) +#define HDMI_LINK_ISRC2_DATAX (HDMI_ADDR_OFFSET + 0x000106A0) +#define HDMI_LINK_AVI_CON (HDMI_ADDR_OFFSET + 0x00010700) +#define HDMI_LINK_AVI_HEADER0 (HDMI_ADDR_OFFSET + 0x00010710) +#define HDMI_LINK_AVI_HEADER1 (HDMI_ADDR_OFFSET + 0x00010714) +#define HDMI_LINK_AVI_HEADER2 (HDMI_ADDR_OFFSET + 0x00010718) +#define HDMI_LINK_AVI_CHECK_SUM (HDMI_ADDR_OFFSET + 0x0001071C) +#define HDMI_LINK_AVI_BYTEX (HDMI_ADDR_OFFSET + 0x00010720) +#define HDMI_LINK_AVI_BYTE00 (HDMI_ADDR_OFFSET + 0x00010720) +#define HDMI_LINK_AVI_BYTE01 (HDMI_ADDR_OFFSET + 0x00010724) +#define HDMI_LINK_AVI_BYTE02 (HDMI_ADDR_OFFSET + 0x00010728) +#define HDMI_LINK_AVI_BYTE03 (HDMI_ADDR_OFFSET + 0x0001073C) +#define HDMI_LINK_AVI_BYTE04 (HDMI_ADDR_OFFSET + 0x00010730) +#define HDMI_LINK_AVI_BYTE05 (HDMI_ADDR_OFFSET + 0x00010734) +#define HDMI_LINK_AVI_BYTE06 (HDMI_ADDR_OFFSET + 0x00010738) +#define HDMI_LINK_AVI_BYTE07 (HDMI_ADDR_OFFSET + 0x0001074C) +#define HDMI_LINK_AVI_BYTE08 (HDMI_ADDR_OFFSET + 0x00010740) +#define HDMI_LINK_AVI_BYTE09 (HDMI_ADDR_OFFSET + 0x00010744) +#define HDMI_LINK_AVI_BYTE10 (HDMI_ADDR_OFFSET + 0x00010748) +#define HDMI_LINK_AVI_BYTE11 (HDMI_ADDR_OFFSET + 0x0001074C) +#define HDMI_LINK_AVI_BYTE12 (HDMI_ADDR_OFFSET + 0x00010750) +#define HDMI_LINK_AUI_CON (HDMI_ADDR_OFFSET + 0x00010800) +#define HDMI_LINK_AUI_HEADER0 (HDMI_ADDR_OFFSET + 0x00010810) +#define HDMI_LINK_AUI_HEADER1 (HDMI_ADDR_OFFSET + 0x00010814) +#define HDMI_LINK_AUI_HEADER2 (HDMI_ADDR_OFFSET + 0x00010818) +#define HDMI_LINK_AUI_CHECK_SUM (HDMI_ADDR_OFFSET + 0x0001081C) +#define HDMI_LINK_AUI_BYTEX (HDMI_ADDR_OFFSET + 0x00010820) +#define HDMI_LINK_MPG_CON (HDMI_ADDR_OFFSET + 0x00010900) +#define HDMI_LINK_MPG_CHECK_SUM (HDMI_ADDR_OFFSET + 0x0001091C) +#define HDMI_LINK_MPG_DATAX (HDMI_ADDR_OFFSET + 0x00010920) +#define HDMI_LINK_SPD_CON (HDMI_ADDR_OFFSET + 0x00010A00) +#define HDMI_LINK_SPD_HEADER0 (HDMI_ADDR_OFFSET + 0x00010A10) +#define HDMI_LINK_SPD_HEADER1 (HDMI_ADDR_OFFSET + 0x00010A14) +#define HDMI_LINK_SPD_HEADER2 (HDMI_ADDR_OFFSET + 0x00010A18) +#define HDMI_LINK_SPD_DATAX (HDMI_ADDR_OFFSET + 0x00010A20) +#define HDMI_LINK_GAMUT_CON (HDMI_ADDR_OFFSET + 0x00010B00) +#define HDMI_LINK_GAMUT_HEADER0 (HDMI_ADDR_OFFSET + 0x00010B10) +#define HDMI_LINK_GAMUT_HEADER1 (HDMI_ADDR_OFFSET + 0x00010B14) +#define HDMI_LINK_GAMUT_HEADER2 (HDMI_ADDR_OFFSET + 0x00010B18) +#define HDMI_LINK_GAMUT_METADATAX (HDMI_ADDR_OFFSET + 0x00010B20) +#define HDMI_LINK_VSI_CON (HDMI_ADDR_OFFSET + 0x00010C00) +#define HDMI_LINK_VSI_HEADER0 (HDMI_ADDR_OFFSET + 0x00010C10) +#define HDMI_LINK_VSI_HEADER1 (HDMI_ADDR_OFFSET + 0x00010C14) +#define HDMI_LINK_VSI_HEADER2 (HDMI_ADDR_OFFSET + 0x00010C18) +#define HDMI_LINK_VSI_DATAX (HDMI_ADDR_OFFSET + 0x00010C20) +#define HDMI_LINK_VSI_DATA00 (HDMI_ADDR_OFFSET + 0x00010C20) +#define HDMI_LINK_VSI_DATA01 (HDMI_ADDR_OFFSET + 0x00010C24) +#define HDMI_LINK_VSI_DATA02 (HDMI_ADDR_OFFSET + 0x00010C28) +#define HDMI_LINK_VSI_DATA03 (HDMI_ADDR_OFFSET + 0x00010C2C) +#define HDMI_LINK_VSI_DATA04 (HDMI_ADDR_OFFSET + 0x00010C30) +#define HDMI_LINK_VSI_DATA05 (HDMI_ADDR_OFFSET + 0x00010C34) +#define HDMI_LINK_VSI_DATA06 (HDMI_ADDR_OFFSET + 0x00010C38) +#define HDMI_LINK_VSI_DATA07 (HDMI_ADDR_OFFSET + 0x00010C3C) +#define HDMI_LINK_VSI_DATA08 (HDMI_ADDR_OFFSET + 0x00010C40) +#define HDMI_LINK_VSI_DATA09 (HDMI_ADDR_OFFSET + 0x00010C44) +#define HDMI_LINK_VSI_DATA10 (HDMI_ADDR_OFFSET + 0x00010C48) +#define HDMI_LINK_VSI_DATA11 (HDMI_ADDR_OFFSET + 0x00010c4c) +#define HDMI_LINK_VSI_DATA12 (HDMI_ADDR_OFFSET + 0x00010C50) +#define HDMI_LINK_VSI_DATA13 (HDMI_ADDR_OFFSET + 0x00010C54) +#define HDMI_LINK_VSI_DATA14 (HDMI_ADDR_OFFSET + 0x00010C58) +#define HDMI_LINK_VSI_DATA15 (HDMI_ADDR_OFFSET + 0x00010C5c) +#define HDMI_LINK_VSI_DATA16 (HDMI_ADDR_OFFSET + 0x00010C60) +#define HDMI_LINK_VSI_DATA17 (HDMI_ADDR_OFFSET + 0x00010C64) +#define HDMI_LINK_VSI_DATA18 (HDMI_ADDR_OFFSET + 0x00010C68) +#define HDMI_LINK_VSI_DATA19 (HDMI_ADDR_OFFSET + 0x00010C6c) +#define HDMI_LINK_VSI_DATA20 (HDMI_ADDR_OFFSET + 0x00010C70) +#define HDMI_LINK_VSI_DATA21 (HDMI_ADDR_OFFSET + 0x00010c74) +#define HDMI_LINK_VSI_DATA22 (HDMI_ADDR_OFFSET + 0x00010C78) +#define HDMI_LINK_VSI_DATA23 (HDMI_ADDR_OFFSET + 0x00010C7c) +#define HDMI_LINK_VSI_DATA24 (HDMI_ADDR_OFFSET + 0x00010C80) +#define HDMI_LINK_VSI_DATA25 (HDMI_ADDR_OFFSET + 0x00010C84) +#define HDMI_LINK_VSI_DATA26 (HDMI_ADDR_OFFSET + 0x00010C88) +#define HDMI_LINK_VSI_DATA27 (HDMI_ADDR_OFFSET + 0x00010C8C) +#define HDMI_LINK_DC_CONTROL (HDMI_ADDR_OFFSET + 0x00010D00) +#define HDMI_LINK_VIDEO_PATTERN_GEN (HDMI_ADDR_OFFSET + 0x00010D04) +#define HDMI_LINK_AN_SEED_SEL (HDMI_ADDR_OFFSET + 0x00010E48) +#define HDMI_LINK_AN_SEED_0 (HDMI_ADDR_OFFSET + 0x00010E58) +#define HDMI_LINK_AN_SEED_1 (HDMI_ADDR_OFFSET + 0x00010E5C) +#define HDMI_LINK_AN_SEED_2 (HDMI_ADDR_OFFSET + 0x00010E60) +#define HDMI_LINK_AN_SEED_3 (HDMI_ADDR_OFFSET + 0x00010E64) +#define HDMI_LINK_HDCP_SHA1_X (HDMI_ADDR_OFFSET + 0x00017000) + +#define HDMI_LINK_HDCP_SHA1_0_0 (HDMI_LINK_HDCP_SHA1_x + 0x00) +#define HDMI_LINK_HDCP_SHA1_0_1 (HDMI_LINK_HDCP_SHA1_0_0 + 0x04) +#define HDMI_LINK_HDCP_SHA1_0_2 (HDMI_LINK_HDCP_SHA1_0_0 + 0x08) +#define HDMI_LINK_HDCP_SHA1_0_3 (HDMI_LINK_HDCP_SHA1_0_0 + 0x0C) +#define HDMI_LINK_HDCP_SHA1_1_0 (HDMI_LINK_HDCP_SHA1_x + 0x10) +#define HDMI_LINK_HDCP_SHA1_1_1 (HDMI_LINK_HDCP_SHA1_1_0 + 0x04) +#define HDMI_LINK_HDCP_SHA1_1_2 (HDMI_LINK_HDCP_SHA1_1_0 + 0x08) +#define HDMI_LINK_HDCP_SHA1_1_3 (HDMI_LINK_HDCP_SHA1_1_0 + 0x0C) +#define HDMI_LINK_HDCP_SHA1_2_0 (HDMI_LINK_HDCP_SHA1_x + 0x20) +#define HDMI_LINK_HDCP_SHA1_2_1 (HDMI_LINK_HDCP_SHA1_2_0 + 0x04) +#define HDMI_LINK_HDCP_SHA1_2_2 (HDMI_LINK_HDCP_SHA1_2_0 + 0x08) +#define HDMI_LINK_HDCP_SHA1_2_3 (HDMI_LINK_HDCP_SHA1_2_0 + 0x0C) +#define HDMI_LINK_HDCP_SHA1_3_0 (HDMI_LINK_HDCP_SHA1_x + 0x30) +#define HDMI_LINK_HDCP_SHA1_3_1 (HDMI_LINK_HDCP_SHA1_3_0 + 0x04) +#define HDMI_LINK_HDCP_SHA1_3_2 (HDMI_LINK_HDCP_SHA1_3_0 + 0x08) +#define HDMI_LINK_HDCP_SHA1_3_3 (HDMI_LINK_HDCP_SHA1_3_0 + 0x0C) +#define HDMI_LINK_HDCP_SHA1_4_0 (HDMI_LINK_HDCP_SHA1_x + 0x40) +#define HDMI_LINK_HDCP_SHA1_4_1 (HDMI_LINK_HDCP_SHA1_4_0 + 0x04) +#define HDMI_LINK_HDCP_SHA1_4_2 (HDMI_LINK_HDCP_SHA1_4_0 + 0x08) +#define HDMI_LINK_HDCP_SHA1_4_3 (HDMI_LINK_HDCP_SHA1_4_0 + 0x0C) + +#define HDMI_LINK_HDCP_KSV_LIST_X (HDMI_ADDR_OFFSET + 0x00017050) + +#define HDMI_LINK_HDCP_KSV_0_0 (HDMI_LINK_HDCP_KSV_LIST_X + 0x00) +#define HDMI_LINK_HDCP_KSV_0_1 (HDMI_LINK_HDCP_KSV_LIST_X + 0x04) +#define HDMI_LINK_HDCP_KSV_0_2 (HDMI_LINK_HDCP_KSV_LIST_X + 0x08) +#define HDMI_LINK_HDCP_KSV_0_3 (HDMI_LINK_HDCP_KSV_LIST_X + 0x0C) +#define HDMI_LINK_HDCP_KSV_1_0 (HDMI_LINK_HDCP_KSV_LIST_X + 0x10) +#define HDMI_LINK_HDCP_KSV_1_1 (HDMI_LINK_HDCP_KSV_LIST_X + 0x14) + +#define HDMI_LINK_HDCP_KSV_LIST_0_0 (HDMI_LINK_HDCP_KSV_LIST_X + 0x00) +#define HDMI_LINK_HDCP_KSV_LIST_0_1 (HDMI_LINK_HDCP_KSV_LIST_X + 0x04) +#define HDMI_LINK_HDCP_KSV_LIST_0_2 (HDMI_LINK_HDCP_KSV_LIST_X + 0x08) +#define HDMI_LINK_HDCP_KSV_LIST_0_3 (HDMI_LINK_HDCP_KSV_LIST_X + 0x0C) +#define HDMI_LINK_HDCP_KSV_LIST_1_0 (HDMI_LINK_HDCP_KSV_LIST_X + 0x10) +#define HDMI_LINK_HDCP_KSV_LIST_1_1 (HDMI_LINK_HDCP_KSV_LIST_X + 0x14) + +#define HDMI_LINK_HDCP_KSV_LIST_CON (HDMI_ADDR_OFFSET + 0x00017064) +#define HDMI_LINK_HDCP_SHA_RESULT (HDMI_ADDR_OFFSET + 0x00017070) +#define HDMI_LINK_HDCP_CTRL1 (HDMI_ADDR_OFFSET + 0x00017080) +#define HDMI_LINK_HDCP_CTRL2 (HDMI_ADDR_OFFSET + 0x00017084) +#define HDMI_LINK_HDCP_CHECK_RESULT (HDMI_ADDR_OFFSET + 0x00017090) +#define HDMI_LINK_HDCP_BKSV_X (HDMI_ADDR_OFFSET + 0x000170A0) + +#define HDMI_LINK_HDCP_BKSV0_0 (HDMI_ADDR_OFFSET + 0x000170A0) +#define HDMI_LINK_HDCP_BKSV0_1 (HDMI_ADDR_OFFSET + 0x000170A4) +#define HDMI_LINK_HDCP_BKSV0_2 (HDMI_ADDR_OFFSET + 0x000170A8) +#define HDMI_LINK_HDCP_BKSV0_3 (HDMI_ADDR_OFFSET + 0x000170AC) +#define HDMI_LINK_HDCP_BKSV1 (HDMI_ADDR_OFFSET + 0x000170B0) + +#define HDMI_LINK_HDCP_AKSV_X (HDMI_ADDR_OFFSET + 0x000170C0) +#define HDMI_LINK_HDCP_AN_X (HDMI_ADDR_OFFSET + 0x000170E0) +#define HDMI_LINK_HDCP_BCAPS (HDMI_ADDR_OFFSET + 0x00017100) +#define HDMI_LINK_HDCP_BSTATUS_0 (HDMI_ADDR_OFFSET + 0x00017110) +#define HDMI_LINK_HDCP_BSTATUS_1 (HDMI_ADDR_OFFSET + 0x00017114) +#define HDMI_LINK_HDCP_RI_0 (HDMI_ADDR_OFFSET + 0x00017140) +#define HDMI_LINK_HDCP_RI_1 (HDMI_ADDR_OFFSET + 0x00017144) + +#define HDMI_LINK_HDCP_OFFSET_TX_0 (HDMI_ADDR_OFFSET + 0x00017160) +#define HDMI_LINK_HDCP_OFFSET_TX_1 (HDMI_ADDR_OFFSET + 0x00017164) +#define HDMI_LINK_HDCP_OFFSET_TX_2 (HDMI_ADDR_OFFSET + 0x00017168) +#define HDMI_LINK_HDCP_OFFSET_TX_3 (HDMI_ADDR_OFFSET + 0x0001716C) +#define HDMI_LINK_HDCP_CYCLE_AA (HDMI_ADDR_OFFSET + 0x00017170) + +#define HDMI_LINK_HDCP_I2C_INT (HDMI_ADDR_OFFSET + 0x00017180) +#define HDMI_LINK_HDCP_AN_INT (HDMI_ADDR_OFFSET + 0x00017190) +#define HDMI_LINK_HDCP_WATCHDOG_INT (HDMI_ADDR_OFFSET + 0x000171A0) +#define HDMI_LINK_HDCP_RI_INT (HDMI_ADDR_OFFSET + 0x000171B0) +#define HDMI_LINK_HDCP_RI_COMPARE_0 (HDMI_ADDR_OFFSET + 0x000171D0) +#define HDMI_LINK_HDCP_RI_COMPARE_1 (HDMI_ADDR_OFFSET + 0x000171D4) + +#define HDMI_LINK_HDCP_RI_INT (HDMI_ADDR_OFFSET + 0x000171B0) +#define HDMI_LINK_HDCP_RI_COMPARE_0 (HDMI_ADDR_OFFSET + 0x000171D0) +#define HDMI_LINK_HDCP_RI_COMPARE_1 (HDMI_ADDR_OFFSET + 0x000171D4) + +#define HDMI_LINK_HDCP_FRAME_COUNT (HDMI_ADDR_OFFSET + 0x000171E0) +#define HDMI_LINK_RGB_ROUND_EN (HDMI_ADDR_OFFSET + 0x0001D500) +#define HDMI_LINK_VACT_SPACE_R_0 (HDMI_ADDR_OFFSET + 0x0001D504) +#define HDMI_LINK_VACT_SPACE_R_1 (HDMI_ADDR_OFFSET + 0x0001D508) +#define HDMI_LINK_VACT_SPACE_G_0 (HDMI_ADDR_OFFSET + 0x0001D50C) +#define HDMI_LINK_VACT_SPACE_G_1 (HDMI_ADDR_OFFSET + 0x0001D510) +#define HDMI_LINK_VACT_SPACE_B_0 (HDMI_ADDR_OFFSET + 0x0001D514) +#define HDMI_LINK_VACT_SPACE_B_1 (HDMI_ADDR_OFFSET + 0x0001D518) +#define HDMI_LINK_BLUE_SCREEN_R_0 (HDMI_ADDR_OFFSET + 0x0001D520) +#define HDMI_LINK_BLUE_SCREEN_R_1 (HDMI_ADDR_OFFSET + 0x0001D524) +#define HDMI_LINK_BLUE_SCREEN_G_0 (HDMI_ADDR_OFFSET + 0x0001D528) +#define HDMI_LINK_BLUE_SCREEN_G_1 (HDMI_ADDR_OFFSET + 0x0001D52C) +#define HDMI_LINK_BLUE_SCREEN_B_0 (HDMI_ADDR_OFFSET + 0x0001D530) +#define HDMI_LINK_BLUE_SCREEN_B_1 (HDMI_ADDR_OFFSET + 0x0001D534) +#define HDMI_LINK_AES_START (HDMI_ADDR_OFFSET + 0x00020000) +#define HDMI_LINK_AES_DATA_SIZE_L (HDMI_ADDR_OFFSET + 0x00020020) +#define HDMI_LINK_AES_DATA_SIZE_H (HDMI_ADDR_OFFSET + 0x00020024) +#define HDMI_LINK_AES_DATA (HDMI_ADDR_OFFSET + 0x00020040) +#define HDMI_LINK_SPDIFIN_CLK_CTRL (HDMI_ADDR_OFFSET + 0x00030000) +#define HDMI_LINK_SPDIFIN_OP_CTRL (HDMI_ADDR_OFFSET + 0x00030004) +#define HDMI_LINK_SPDIFIN_IRQ_MASK (HDMI_ADDR_OFFSET + 0x00030008) +#define HDMI_LINK_SPDIFIN_IRQ_STATUS (HDMI_ADDR_OFFSET + 0x0003000C) +#define HDMI_LINK_SPDIFIN_CONFIG_1 (HDMI_ADDR_OFFSET + 0x00030010) +#define HDMI_LINK_SPDIFIN_CONFIG_2 (HDMI_ADDR_OFFSET + 0x00030014) +#define HDMI_LINK_SPDIFIN_USER_VALUE_1 (HDMI_ADDR_OFFSET + 0x00030020) +#define HDMI_LINK_SPDIFIN_USER_VALUE_2 (HDMI_ADDR_OFFSET + 0x00030024) +#define HDMI_LINK_SPDIFIN_USER_VALUE_3 (HDMI_ADDR_OFFSET + 0x00030028) +#define HDMI_LINK_SPDIFIN_USER_VALUE_4 (HDMI_ADDR_OFFSET + 0x0003002C) +#define HDMI_LINK_SPDIFIN_CH_STATUS_0_1 (HDMI_ADDR_OFFSET + 0x00030030) +#define HDMI_LINK_SPDIFIN_CH_STATUS_0_2 (HDMI_ADDR_OFFSET + 0x00030034) +#define HDMI_LINK_SPDIFIN_CH_STATUS_0_3 (HDMI_ADDR_OFFSET + 0x00030038) +#define HDMI_LINK_SPDIFIN_CH_STATUS_0_4 (HDMI_ADDR_OFFSET + 0x0003003C) +#define HDMI_LINK_SPDIFIN_CH_STATUS_1 (HDMI_ADDR_OFFSET + 0x00030040) +#define HDMI_LINK_SPDIFIN_FRAME_PERIOD_1 (HDMI_ADDR_OFFSET + 0x00030048) +#define HDMI_LINK_SPDIFIN_FRAME_PERIOD_2 (HDMI_ADDR_OFFSET + 0x0003004C) +#define HDMI_LINK_SPDIFIN_PC_INFO_1 (HDMI_ADDR_OFFSET + 0x00030050) +#define HDMI_LINK_SPDIFIN_PC_INFO_2 (HDMI_ADDR_OFFSET + 0x00030054) +#define HDMI_LINK_SPDIFIN_PD_INFO_1 (HDMI_ADDR_OFFSET + 0x00030058) +#define HDMI_LINK_SPDIFIN_PD_INFO_2 (HDMI_ADDR_OFFSET + 0x0003005C) +#define HDMI_LINK_SPDIFIN_DATA_BUF_0_1 (HDMI_ADDR_OFFSET + 0x00030060) +#define HDMI_LINK_SPDIFIN_DATA_BUF_0_2 (HDMI_ADDR_OFFSET + 0x00030064) +#define HDMI_LINK_SPDIFIN_DATA_BUF_0_3 (HDMI_ADDR_OFFSET + 0x00030068) +#define HDMI_LINK_SPDIFIN_USER_BUF_0 (HDMI_ADDR_OFFSET + 0x0003006C) +#define HDMI_LINK_SPDIFIN_DATA_BUF_1_1 (HDMI_ADDR_OFFSET + 0x00030070) +#define HDMI_LINK_SPDIFIN_DATA_BUF_1_2 (HDMI_ADDR_OFFSET + 0x00030074) +#define HDMI_LINK_SPDIFIN_DATA_BUF_1_3 (HDMI_ADDR_OFFSET + 0x00030078) +#define HDMI_LINK_SPDIFIN_USER_BUF_1 (HDMI_ADDR_OFFSET + 0x0003007C) +#define HDMI_LINK_I2S_CLK_CON (HDMI_ADDR_OFFSET + 0x00040000) +#define HDMI_LINK_I2S_CON_1 (HDMI_ADDR_OFFSET + 0x00040004) +#define HDMI_LINK_I2S_CON_2 (HDMI_ADDR_OFFSET + 0x00040008) +#define HDMI_LINK_I2S_PIN_SEL_0 (HDMI_ADDR_OFFSET + 0x0004000C) +#define HDMI_LINK_I2S_PIN_SEL_1 (HDMI_ADDR_OFFSET + 0x00040010) +#define HDMI_LINK_I2S_PIN_SEL_2 (HDMI_ADDR_OFFSET + 0x00040014) +#define HDMI_LINK_I2S_PIN_SEL_3 (HDMI_ADDR_OFFSET + 0x00040018) +#define HDMI_LINK_I2S_DSD_CON (HDMI_ADDR_OFFSET + 0x0004001C) +#define HDMI_LINK_I2S_MUX_CON (HDMI_ADDR_OFFSET + 0x00040020) +#define HDMI_LINK_I2S_CH_ST_CON (HDMI_ADDR_OFFSET + 0x00040024) +#define HDMI_LINK_I2S_CH_ST_0 (HDMI_ADDR_OFFSET + 0x00040028) +#define HDMI_LINK_I2S_CH_ST_1 (HDMI_ADDR_OFFSET + 0x0004002C) +#define HDMI_LINK_I2S_CH_ST_2 (HDMI_ADDR_OFFSET + 0x00040030) +#define HDMI_LINK_I2S_CH_ST_3 (HDMI_ADDR_OFFSET + 0x00040034) +#define HDMI_LINK_I2S_CH_ST_4 (HDMI_ADDR_OFFSET + 0x00040038) +#define HDMI_LINK_I2S_CH_ST_SH_0 (HDMI_ADDR_OFFSET + 0x0004003C) +#define HDMI_LINK_I2S_CH_ST_SH_1 (HDMI_ADDR_OFFSET + 0x00040040) +#define HDMI_LINK_I2S_CH_ST_SH_2 (HDMI_ADDR_OFFSET + 0x00040044) +#define HDMI_LINK_I2S_CH_ST_SH_3 (HDMI_ADDR_OFFSET + 0x00040048) +#define HDMI_LINK_I2S_CH_ST_SH_4 (HDMI_ADDR_OFFSET + 0x0004004C) +#define HDMI_LINK_I2S_VD_DATA (HDMI_ADDR_OFFSET + 0x00040050) +#define HDMI_LINK_I2S_MUX_CH (HDMI_ADDR_OFFSET + 0x00040054) +#define HDMI_LINK_I2S_MUX_CUV (HDMI_ADDR_OFFSET + 0x00040058) +#define HDMI_LINK_I2S_CH0_L_0 (HDMI_ADDR_OFFSET + 0x00040064) +#define HDMI_LINK_I2S_CH0_L_1 (HDMI_ADDR_OFFSET + 0x00040068) +#define HDMI_LINK_I2S_CH0_L_2 (HDMI_ADDR_OFFSET + 0x0004006C) +#define HDMI_LINK_I2S_CH0_R_0 (HDMI_ADDR_OFFSET + 0x00040074) +#define HDMI_LINK_I2S_CH0_R_1 (HDMI_ADDR_OFFSET + 0x00040078) +#define HDMI_LINK_I2S_CH0_R_2 (HDMI_ADDR_OFFSET + 0x0004007C) +#define HDMI_LINK_I2S_CH0_R_3 (HDMI_ADDR_OFFSET + 0x00040080) +#define HDMI_LINK_I2S_CH1_L_0 (HDMI_ADDR_OFFSET + 0x00040084) +#define HDMI_LINK_I2S_CH1_L_1 (HDMI_ADDR_OFFSET + 0x00040088) +#define HDMI_LINK_I2S_CH1_L_2 (HDMI_ADDR_OFFSET + 0x0004008C) +#define HDMI_LINK_I2S_CH1_L_3 (HDMI_ADDR_OFFSET + 0x00040090) +#define HDMI_LINK_I2S_CH1_R_0 (HDMI_ADDR_OFFSET + 0x00040094) +#define HDMI_LINK_I2S_CH1_R_1 (HDMI_ADDR_OFFSET + 0x00040098) +#define HDMI_LINK_I2S_CH1_R_2 (HDMI_ADDR_OFFSET + 0x0004009C) +#define HDMI_LINK_I2S_CH1_R_3 (HDMI_ADDR_OFFSET + 0x000400A0) +#define HDMI_LINK_I2S_CH2_L_0 (HDMI_ADDR_OFFSET + 0x000400A4) +#define HDMI_LINK_I2S_CH2_L_1 (HDMI_ADDR_OFFSET + 0x000400A8) +#define HDMI_LINK_I2S_CH2_L_2 (HDMI_ADDR_OFFSET + 0x000400AC) +#define HDMI_LINK_I2S_CH2_L_3 (HDMI_ADDR_OFFSET + 0x000400B0) +#define HDMI_LINK_I2S_CH2_R_0 (HDMI_ADDR_OFFSET + 0x000400B4) +#define HDMI_LINK_I2S_CH2_R_1 (HDMI_ADDR_OFFSET + 0x000400B8) +#define HDMI_LINK_I2S_CH2_R_2 (HDMI_ADDR_OFFSET + 0x000400BC) +#define HDMI_LINK_I2S_CH2_R_3 (HDMI_ADDR_OFFSET + 0x000400C0) +#define HDMI_LINK_I2S_CH3_L_0 (HDMI_ADDR_OFFSET + 0x000400C4) +#define HDMI_LINK_I2S_CH3_L_1 (HDMI_ADDR_OFFSET + 0x000400C8) +#define HDMI_LINK_I2S_CH3_L_2 (HDMI_ADDR_OFFSET + 0x000400CC) +#define HDMI_LINK_I2S_CH3_R_0 (HDMI_ADDR_OFFSET + 0x000400D0) +#define HDMI_LINK_I2S_CH3_R_1 (HDMI_ADDR_OFFSET + 0x000400D4) +#define HDMI_LINK_I2S_CH3_R_2 (HDMI_ADDR_OFFSET + 0x000400D8) +#define HDMI_LINK_I2S_CUV_L_R (HDMI_ADDR_OFFSET + 0x000400DC) + +#define HDMI_CEC_TX_STATUS_0 (OTHER_ADDR_OFFSET + 0x00000000) +#define HDMI_CEC_TX_STATUS_1 (OTHER_ADDR_OFFSET + 0x00000004) +#define HDMI_CEC_RX_STATUS_0 (OTHER_ADDR_OFFSET + 0x00000008) +#define HDMI_CEC_RX_STATUS_1 (OTHER_ADDR_OFFSET + 0x0000000C) +#define HDMI_CEC_INTR_MASK (OTHER_ADDR_OFFSET + 0x00000010) +#define HDMI_CEC_INTR_CLEAR (OTHER_ADDR_OFFSET + 0x00000014) +#define HDMI_CEC_LOGIC_ADDR (OTHER_ADDR_OFFSET + 0x00000020) +#define HDMI_CEC_DIVISOR_0 (OTHER_ADDR_OFFSET + 0x00000030) +#define HDMI_CEC_DIVISOR_1 (OTHER_ADDR_OFFSET + 0x00000034) +#define HDMI_CEC_DIVISOR_2 (OTHER_ADDR_OFFSET + 0x00000038) +#define HDMI_CEC_DIVISOR_3 (OTHER_ADDR_OFFSET + 0x0000003C) +#define HDMI_CEC_TX_CTRL (OTHER_ADDR_OFFSET + 0x00000040) +#define HDMI_CEC_TX_BYTE_NUM (OTHER_ADDR_OFFSET + 0x00000044) +#define HDMI_CEC_TX_STATUS_2 (OTHER_ADDR_OFFSET + 0x00000060) +#define HDMI_CEC_TX_STATUS_3 (OTHER_ADDR_OFFSET + 0x00000064) +#define HDMI_CEC_TX_BUFFER_x (OTHER_ADDR_OFFSET + 0x00000080) +#define HDMI_CEC_TX_BUFFER00 (OTHER_ADDR_OFFSET + 0x00000080) +#define HDMI_CEC_RX_CTRL (OTHER_ADDR_OFFSET + 0x000000C0) +#define HDMI_CEC_RX_STATUS_2 (OTHER_ADDR_OFFSET + 0x000000E0) +#define HDMI_CEC_RX_STATUS_3 (OTHER_ADDR_OFFSET + 0x000000E4) +#define HDMI_CEC_RX_BUFFER_x (OTHER_ADDR_OFFSET + 0x00000100) +#define HDMI_CEC_FILTER_CTRL (OTHER_ADDR_OFFSET + 0x00000180) +#define HDMI_CEC_FILTER_TH (OTHER_ADDR_OFFSET + 0x00000184) + +#ifdef CONFIG_MACH_S5P6818 +#define HDMI_PHY_OFFSET \ + (PHY_BASEADDR_HDMI_PHY_MODULE - PHY_BASEADDR_HDMI_MODULE) +#else +#define HDMI_PHY_OFFSET 0x400 +#endif + +#define HDMI_PHY_REG00 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000000) +#define HDMI_PHY_REG04 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000004) +#define HDMI_PHY_REG08 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000008) +#define HDMI_PHY_REG0C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000000C) +#define HDMI_PHY_REG10 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000010) +#define HDMI_PHY_REG14 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000014) +#define HDMI_PHY_REG18 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000018) +#define HDMI_PHY_REG1C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000001C) +#define HDMI_PHY_REG20 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000020) +#define HDMI_PHY_REG24 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000024) +#define HDMI_PHY_REG28 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000028) +#define HDMI_PHY_REG2C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000002C) +#define HDMI_PHY_REG30 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000030) +#define HDMI_PHY_REG34 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000034) +#define HDMI_PHY_REG38 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000038) +#define HDMI_PHY_REG3C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000003C) +#define HDMI_PHY_REG40 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000040) +#define HDMI_PHY_REG44 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000044) +#define HDMI_PHY_REG48 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000048) +#define HDMI_PHY_REG4C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000004C) +#define HDMI_PHY_REG50 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000050) +#define HDMI_PHY_REG54 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000054) +#define HDMI_PHY_REG58 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000058) +#define HDMI_PHY_REG5C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000005C) +#define HDMI_PHY_REG60 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000060) +#define HDMI_PHY_REG64 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000064) +#define HDMI_PHY_REG68 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000068) +#define HDMI_PHY_REG6C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000006C) +#define HDMI_PHY_REG70 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000070) +#define HDMI_PHY_REG74 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000074) +#define HDMI_PHY_REG78 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000078) +#define HDMI_PHY_REG7C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000007C) +#define HDMI_PHY_REG80 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000080) +#define HDMI_PHY_REG84 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000084) +#define HDMI_PHY_REG88 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000088) +#define HDMI_PHY_REG8C (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x0000008C) +#define HDMI_PHY_REG90 (OTHER_ADDR_OFFSET + HDMI_PHY_OFFSET + 0x00000090) + +enum hdmi_reset { + i_nRST = 0, + i_nRST_VIDEO = 1, + i_nRST_SPDIF = 2, + i_nRST_TMDS = 3, + i_nRST_PHY = 4, +}; + +u32 nx_hdmi_get_reg(u32 module_index, u32 offset); +void nx_hdmi_set_reg(u32 module_index, u32 offset, u32 regvalue); + +void nx_hdmi_set_base_address(u32 module_index, void *base_address); +void *nx_hdmi_get_base_address(u32 module_index); +u32 nx_hdmi_get_physical_address(u32 module_index); + +#endif diff --git a/drivers/video/nexell/soc/s5pxx18_soc_lvds.c b/drivers/video/nexell/soc/s5pxx18_soc_lvds.c new file mode 100644 index 0000000..18c101b --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_lvds.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <linux/types.h> +#include <linux/io.h> + +#include "s5pxx18_soc_disptop.h" +#include "s5pxx18_soc_lvds.h" + +#ifndef pow +static inline unsigned int pow(int a, int b) +{ + if (b == 0) + return 1; + else + return a * pow(a, b - 1); +} +#endif + +static struct nx_lvds_register_set *__g_pregister[NUMBER_OF_LVDS_MODULE]; + +int nx_lvds_initialize(void) +{ + static int binit; + u32 i; + + if (binit == 0) { + for (i = 0; i < NUMBER_OF_LVDS_MODULE; i++) + __g_pregister[i] = NULL; + binit = 1; + } + + return 1; +} + +u32 nx_lvds_get_number_of_module(void) +{ + return NUMBER_OF_LVDS_MODULE; +} + +u32 nx_lvds_get_size_of_register_set(void) +{ + return sizeof(struct nx_lvds_register_set); +} + +void nx_lvds_set_base_address(u32 module_index, void *base_address) +{ + __g_pregister[module_index] = + (struct nx_lvds_register_set *)base_address; +} + +void *nx_lvds_get_base_address(u32 module_index) +{ + return (void *)__g_pregister[module_index]; +} + +u32 nx_lvds_get_physical_address(u32 module_index) +{ + const u32 physical_addr[] = PHY_BASEADDR_LVDS_LIST; + + return physical_addr[module_index]; +} + +int nx_lvds_open_module(u32 module_index) +{ + return true; +} + +int nx_lvds_close_module(u32 module_index) +{ + return true; +} + +int nx_lvds_check_busy(u32 module_index) +{ + return false; +} + +void nx_lvds_set_lvdsctrl0(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsctrl0); +} + +void nx_lvds_set_lvdsctrl1(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsctrl1); +} + +void nx_lvds_set_lvdsctrl2(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsctrl2); +} + +void nx_lvds_set_lvdsctrl3(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsctrl3); +} + +void nx_lvds_set_lvdsctrl4(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsctrl4); +} + +void nx_lvds_set_lvdstmode0(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdstmode0); +} + +void nx_lvds_set_lvdsloc0(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsloc0); +} + +void nx_lvds_set_lvdsloc1(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsloc1); +} + +void nx_lvds_set_lvdsloc2(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsloc2); +} + +void nx_lvds_set_lvdsloc3(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsloc3); +} + +void nx_lvds_set_lvdsloc4(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsloc4); +} + +void nx_lvds_set_lvdsloc5(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsloc5); +} + +void nx_lvds_set_lvdsloc6(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdsloc6); +} + +void nx_lvds_set_lvdslocmask0(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdslocmask0); +} + +void nx_lvds_set_lvdslocmask1(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdslocmask1); +} + +void nx_lvds_set_lvdslocpol0(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdslocpol0); +} + +void nx_lvds_set_lvdslocpol1(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + writel(regvalue, &pregister->lvdslocpol1); +} + +void nx_lvds_set_lvdsdummy(u32 module_index, u32 regvalue) +{ + register struct nx_lvds_register_set *pregister; + u32 oldvalue; + + pregister = __g_pregister[module_index]; + oldvalue = readl(&pregister->lvdsctrl1) & 0x00ffffff; + writel(oldvalue | ((regvalue & 0xff) << 24), &pregister->lvdsctrl1); +} + +u32 nx_lvds_get_lvdsdummy(u32 module_index) +{ + register struct nx_lvds_register_set *pregister; + u32 oldvalue; + + pregister = __g_pregister[module_index]; + oldvalue = readl(&pregister->lvdsctrl1); + oldvalue = oldvalue >> 24; + return oldvalue; +} + +u32 nx_lvds_get_lvdsctrl0(u32 module_index) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + return (u32)readl(&pregister->lvdsctrl0); +} + +u32 nx_lvds_get_lvdsctrl1(u32 module_index) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + return (u32)readl(&pregister->lvdsctrl1); +} + +u32 nx_lvds_get_lvdsctrl2(u32 module_index) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + return (u32)readl(&pregister->lvdsctrl2); +} + +u32 nx_lvds_get_lvdsctrl3(u32 module_index) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + return (u32)readl(&pregister->lvdsctrl3); +} + +u32 nx_lvds_get_lvdsctrl4(u32 module_index) +{ + register struct nx_lvds_register_set *pregister; + + pregister = __g_pregister[module_index]; + return (u32)readl(&pregister->lvdsctrl4); +} diff --git a/drivers/video/nexell/soc/s5pxx18_soc_lvds.h b/drivers/video/nexell/soc/s5pxx18_soc_lvds.h new file mode 100644 index 0000000..08f8e5c --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_lvds.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _S5PXX18_SOC_LVDS_H_ +#define _S5PXX18_SOC_LVDS_H_ + +/* + * refter to s5pxx18_soc_disptop.h + * + * #define NUMBER_OF_LVDS_MODULE 1 + * #define PHY_BASEADDR_LVDS_MODULE 0xC010A000 + */ +#define PHY_BASEADDR_LVDS_LIST \ + { PHY_BASEADDR_LVDS_MODULE } + +struct nx_lvds_register_set { + u32 lvdsctrl0; + u32 lvdsctrl1; + u32 lvdsctrl2; + u32 lvdsctrl3; + u32 lvdsctrl4; + u32 _reserved0[3]; + u32 lvdsloc0; + u32 lvdsloc1; + u32 lvdsloc2; + u32 lvdsloc3; + u32 lvdsloc4; + u32 lvdsloc5; + u32 lvdsloc6; + u32 _reserved1; + u32 lvdslocmask0; + u32 lvdslocmask1; + u32 lvdslocpol0; + u32 lvdslocpol1; + u32 lvdstmode0; + u32 lvdstmode1; + u32 _reserved2[2]; +}; + +int nx_lvds_initialize(void); +u32 nx_lvds_get_number_of_module(void); +u32 nx_lvds_get_size_of_register_set(void); +void nx_lvds_set_base_address(u32 module_index, void *base_address); +void *nx_lvds_get_base_address(u32 module_index); +u32 nx_lvds_get_physical_address(u32 module_index); +int nx_lvds_open_module(u32 module_index); +int nx_lvds_close_module(u32 module_index); +int nx_lvds_check_busy(u32 module_index); + +void nx_lvds_set_lvdsctrl0(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsctrl1(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsctrl2(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsctrl3(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsctrl4(u32 module_index, u32 regvalue); +u32 nx_lvds_get_lvdsctrl0(u32 module_index); +u32 nx_lvds_get_lvdsctrl1(u32 module_index); +u32 nx_lvds_get_lvdsctrl2(u32 module_index); +u32 nx_lvds_get_lvdsctrl3(u32 module_index); +u32 nx_lvds_get_lvdsctrl4(u32 module_index); + +void nx_lvds_set_lvdstmode0(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsloc0(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsloc1(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsloc2(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsloc3(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsloc4(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsloc5(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdsloc6(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdslocmask0(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdslocmask1(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdslocpol0(u32 module_index, u32 regvalue); +void nx_lvds_set_lvdslocpol1(u32 module_index, u32 regvalue); + +void nx_lvds_set_lvdslocpol1(u32 module_index, u32 regvalue); + +void nx_lvds_set_lvdsdummy(u32 module_index, u32 regvalue); +u32 nx_lvds_get_lvdsdummy(u32 module_index); + +#endif

Low level functions for DPC (Display Controller) and Makefile for all nexell video low level functions.
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
drivers/video/nexell/soc/Makefile | 11 + drivers/video/nexell/soc/s5pxx18_soc_dpc.c | 1569 ++++++++++++++++++++++++++++ drivers/video/nexell/soc/s5pxx18_soc_dpc.h | 444 ++++++++ 3 files changed, 2024 insertions(+) create mode 100644 drivers/video/nexell/soc/Makefile create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_dpc.c create mode 100644 drivers/video/nexell/soc/s5pxx18_soc_dpc.h
diff --git a/drivers/video/nexell/soc/Makefile b/drivers/video/nexell/soc/Makefile new file mode 100644 index 0000000..a3036e5 --- /dev/null +++ b/drivers/video/nexell/soc/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2016 Nexell +# Junghyun, kimjhkim@nexell.co.kr + +obj-$(CONFIG_VIDEO_NX) += s5pxx18_soc_dpc.o s5pxx18_soc_mlc.o \ + s5pxx18_soc_disptop.o s5pxx18_soc_disptop_clk.o + +obj-$(CONFIG_VIDEO_NX_LVDS) += s5pxx18_soc_lvds.o +obj-$(CONFIG_VIDEO_NX_MIPI) += s5pxx18_soc_mipi.o +obj-$(CONFIG_VIDEO_NX_HDMI) += s5pxx18_soc_hdmi.o diff --git a/drivers/video/nexell/soc/s5pxx18_soc_dpc.c b/drivers/video/nexell/soc/s5pxx18_soc_dpc.c new file mode 100644 index 0000000..fc15d6b --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_dpc.c @@ -0,0 +1,1569 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <linux/types.h> +#include <linux/io.h> + +#include "s5pxx18_soc_dpc.h" + +static struct { + struct nx_dpc_register_set *pregister; +} __g_module_variables[NUMBER_OF_DPC_MODULE] = { { NULL,},}; + +int nx_dpc_initialize(void) +{ + static int binit; + u32 i; + + if (binit == 0) { + for (i = 0; i < NUMBER_OF_DPC_MODULE; i++) + __g_module_variables[i].pregister = NULL; + binit = 1; + } + return 1; +} + +u32 nx_dpc_get_number_of_module(void) +{ + return NUMBER_OF_DPC_MODULE; +} + +u32 nx_dpc_get_physical_address(u32 module_index) +{ + const u32 physical_addr[] = PHY_BASEADDR_DPC_LIST; + + return physical_addr[module_index]; +} + +void nx_dpc_set_base_address(u32 module_index, void *base_address) +{ + __g_module_variables[module_index].pregister = + (struct nx_dpc_register_set *)base_address; +} + +void *nx_dpc_get_base_address(u32 module_index) +{ + return (void *)__g_module_variables[module_index].pregister; +} + +void nx_dpc_set_interrupt_enable(u32 module_index, int32_t int_num, int enable) +{ + const u32 intenb_pos = 11; + const u32 intenb_mask = 1ul << intenb_pos; + const u32 intpend_pos = 10; + const u32 intpend_mask = 1ul << intpend_pos; + + register u32 regvalue; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->dpcctrl0; + regvalue &= ~(intenb_mask | intpend_mask); + regvalue |= (u32)enable << intenb_pos; + + writel(regvalue, &pregister->dpcctrl0); +} + +int nx_dpc_get_interrupt_enable(u32 module_index, int32_t int_num) +{ + const u32 intenb_pos = 11; + const u32 intenb_mask = 1ul << intenb_pos; + + return (int)((__g_module_variables[module_index].pregister->dpcctrl0 & + intenb_mask) >> intenb_pos); +} + +void nx_dpc_set_interrupt_enable32(u32 module_index, u32 enable_flag) +{ + const u32 intenb_pos = 11; + const u32 intenb_mask = 1 << intenb_pos; + const u32 intpend_pos = 10; + const u32 intpend_mask = 1 << intpend_pos; + + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcctrl0 & ~(intpend_mask | intenb_mask); + + writel((u32)(read_value | (enable_flag & 0x01) << intenb_pos), + &pregister->dpcctrl0); +} + +u32 nx_dpc_get_interrupt_enable32(u32 module_index) +{ + const u32 intenb_pos = 11; + const u32 intenb_mask = 1 << intenb_pos; + + return (u32)((__g_module_variables[module_index].pregister->dpcctrl0 & + intenb_mask) >> intenb_pos); +} + +int nx_dpc_get_interrupt_pending(u32 module_index, int32_t int_num) +{ + const u32 intpend_pos = 10; + const u32 intpend_mask = 1ul << intpend_pos; + + return (int)((__g_module_variables[module_index].pregister->dpcctrl0 & + intpend_mask) >> intpend_pos); +} + +u32 nx_dpc_get_interrupt_pending32(u32 module_index) +{ + const u32 intpend_pos = 10; + const u32 intpend_mask = 1 << intpend_pos; + + return (u32)((__g_module_variables[module_index].pregister->dpcctrl0 & + intpend_mask) >> intpend_pos); +} + +void nx_dpc_clear_interrupt_pending(u32 module_index, int32_t int_num) +{ + const u32 intpend_pos = 10; + register struct nx_dpc_register_set *pregister; + register u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->dpcctrl0; + regvalue |= 1ul << intpend_pos; + + writel(regvalue, &pregister->dpcctrl0); +} + +void nx_dpc_clear_interrupt_pending32(u32 module_index, u32 pending_flag) +{ + const u32 intpend_pos = 10; + const u32 intpend_mask = 1 << intpend_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcctrl0 & ~intpend_mask; + + writel((u32)(read_value | ((pending_flag & 0x01) << intpend_pos)), + &pregister->dpcctrl0); +} + +void nx_dpc_set_interrupt_enable_all(u32 module_index, int enable) +{ + const u32 intenb_pos = 11; + const u32 intenb_mask = 1ul << intenb_pos; + const u32 intpend_pos = 10; + const u32 intpend_mask = 1ul << intpend_pos; + register u32 regvalue; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->dpcctrl0; + regvalue &= ~(intenb_mask | intpend_mask); + regvalue |= (u32)enable << intenb_pos; + + writel(regvalue, &pregister->dpcctrl0); +} + +int nx_dpc_get_interrupt_enable_all(u32 module_index) +{ + const u32 intenb_pos = 11; + const u32 intenb_mask = 1ul << intenb_pos; + + return (int)((__g_module_variables[module_index].pregister->dpcctrl0 & + intenb_mask) >> intenb_pos); +} + +int nx_dpc_get_interrupt_pending_all(u32 module_index) +{ + const u32 intpend_pos = 10; + const u32 intpend_mask = 1ul << intpend_pos; + + return (int)((__g_module_variables[module_index].pregister->dpcctrl0 & + intpend_mask) >> intpend_pos); +} + +void nx_dpc_clear_interrupt_pending_all(u32 module_index) +{ + const u32 intpend_pos = 10; + register struct nx_dpc_register_set *pregister; + register u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + regvalue = pregister->dpcctrl0; + regvalue |= 1ul << intpend_pos; + + writel(regvalue, &pregister->dpcctrl0); +} + +int32_t nx_dpc_get_interrupt_pending_number(u32 module_index) +{ + const u32 intenb_pos = 11; + const u32 intpend_pos = 10; + register struct nx_dpc_register_set *pregister; + register u32 pend; + + pregister = __g_module_variables[module_index].pregister; + pend = ((pregister->dpcctrl0 >> intenb_pos) && + (pregister->dpcctrl0 >> intpend_pos)); + + if (pend & 0x01) + return 0; + + return -1; +} + +void nx_dpc_set_clock_pclk_mode(u32 module_index, enum nx_pclkmode mode) +{ + const u32 pclkmode_pos = 3; + register u32 regvalue; + register struct nx_dpc_register_set *pregister; + u32 clkmode = 0; + + pregister = __g_module_variables[module_index].pregister; + switch (mode) { + case nx_pclkmode_dynamic: + clkmode = 0; + break; + case nx_pclkmode_always: + clkmode = 1; + break; + default: + break; + } + regvalue = pregister->dpcclkenb; + regvalue &= ~(1ul << pclkmode_pos); + regvalue |= (clkmode & 0x01) << pclkmode_pos; + + writel(regvalue, &pregister->dpcclkenb); +} + +enum nx_pclkmode nx_dpc_get_clock_pclk_mode(u32 module_index) +{ + const u32 pclkmode_pos = 3; + + if (__g_module_variables[module_index].pregister->dpcclkenb & + (1ul << pclkmode_pos)) { + return nx_pclkmode_always; + } + return nx_pclkmode_dynamic; +} + +void nx_dpc_set_clock_source(u32 module_index, u32 index, u32 clk_src) +{ + const u32 clksrcsel_pos = 2; + const u32 clksrcsel_mask = 0x07 << clksrcsel_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcclkgen[index][0]; + read_value &= ~clksrcsel_mask; + read_value |= clk_src << clksrcsel_pos; + + writel(read_value, &pregister->dpcclkgen[index][0]); +} + +u32 nx_dpc_get_clock_source(u32 module_index, u32 index) +{ + const u32 clksrcsel_pos = 2; + const u32 clksrcsel_mask = 0x07 << clksrcsel_pos; + + return (__g_module_variables[module_index] + .pregister->dpcclkgen[index][0] & + clksrcsel_mask) >> clksrcsel_pos; +} + +void nx_dpc_set_clock_divisor(u32 module_index, u32 index, u32 divisor) +{ + const u32 clkdiv_pos = 5; + const u32 clkdiv_mask = ((1 << 8) - 1) << clkdiv_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcclkgen[index][0]; + read_value &= ~clkdiv_mask; + read_value |= (divisor - 1) << clkdiv_pos; + + writel(read_value, &pregister->dpcclkgen[index][0]); +} + +u32 nx_dpc_get_clock_divisor(u32 module_index, u32 index) +{ + const u32 clkdiv_pos = 5; + const u32 clkdiv_mask = ((1 << 8) - 1) << clkdiv_pos; + + return ((__g_module_variables[module_index] + .pregister->dpcclkgen[index][0] & + clkdiv_mask) >> clkdiv_pos) + 1; +} + +void nx_dpc_set_clock_out_inv(u32 module_index, u32 index, int out_clk_inv) +{ + const u32 outclkinv_pos = 1; + const u32 outclkinv_mask = 1ul << outclkinv_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcclkgen[index][0]; + read_value &= ~outclkinv_mask; + read_value |= out_clk_inv << outclkinv_pos; + + writel(read_value, &pregister->dpcclkgen[index][0]); +} + +int nx_dpc_get_clock_out_inv(u32 module_index, u32 index) +{ + const u32 outclkinv_pos = 1; + const u32 outclkinv_mask = 1ul << outclkinv_pos; + + return (int)((__g_module_variables[module_index] + .pregister->dpcclkgen[index][0] & + outclkinv_mask) >> outclkinv_pos); +} + +void nx_dpc_set_clock_out_select(u32 module_index, u32 index, int bbypass) +{ + const u32 outclksel_pos = 0; + const u32 outclksel_mask = 1ul << outclksel_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcclkgen[index][0]; + read_value &= ~outclksel_mask; + if (bbypass == 0) + read_value |= outclksel_mask; + + writel(read_value, &pregister->dpcclkgen[index][0]); +} + +int nx_dpc_get_clock_out_select(u32 module_index, u32 index) +{ + const u32 outclksel_pos = 0; + const u32 outclksel_mask = 1ul << outclksel_pos; + + if (__g_module_variables[module_index].pregister->dpcclkgen[index][0] & + outclksel_mask) { + return 0; + } else { + return 1; + } +} + +void nx_dpc_set_clock_polarity(u32 module_index, int bpolarity) +{ + const u32 clkpol_pos = 2; + const u32 clkpol_mask = 1ul << clkpol_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcctrl1; + read_value &= ~clkpol_mask; + if (bpolarity == 1) + read_value |= clkpol_mask; + + writel(read_value, &pregister->dpcctrl1); +} + +int nx_dpc_get_clock_polarity(u32 module_index) +{ + const u32 clkpol_pos = 2; + const u32 clkpol_mask = 1ul << clkpol_pos; + + if (__g_module_variables[module_index].pregister->dpcctrl1 & + clkpol_mask) { + return 1; + } else { + return 0; + } +} + +void nx_dpc_set_clock_out_enb(u32 module_index, u32 index, int out_clk_enb) +{ + const u32 outclkenb_pos = 15; + const u32 outclkenb_mask = 1ul << outclkenb_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcclkgen[index][0]; + read_value &= ~outclkenb_mask; + + if (out_clk_enb == 1) + read_value |= outclkenb_mask; + + writel(read_value, &pregister->dpcclkgen[index][0]); +} + +int nx_dpc_get_clock_out_enb(u32 module_index, u32 index) +{ + const u32 outclkenb_pos = 15; + const u32 outclkenb_mask = 1ul << outclkenb_pos; + + if (__g_module_variables[module_index].pregister->dpcclkgen[index][0] & + outclkenb_mask) { + return 1; + } else { + return 0; + } +} + +void nx_dpc_set_clock_out_delay(u32 module_index, u32 index, u32 delay) +{ + const u32 outclkdelay_pos = 0; + const u32 outclkdelay_mask = 0x1f << outclkdelay_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcclkgen[index][1]; + read_value &= ~outclkdelay_mask; + read_value |= (u32)delay << outclkdelay_pos; + + writel(read_value, &pregister->dpcclkgen[index][1]); +} + +u32 nx_dpc_get_clock_out_delay(u32 module_index, u32 index) +{ + register struct nx_dpc_register_set *pregister; + const u32 outclkdelay_pos = 0; + const u32 outclkdelay_mask = 0x1f << outclkdelay_pos; + + pregister = __g_module_variables[module_index].pregister; + + return (u32)((pregister->dpcclkgen[index][1] & outclkdelay_mask) >> + outclkdelay_pos); +} + +void nx_dpc_set_clock_divisor_enable(u32 module_index, int enable) +{ + const u32 clkgenenb_pos = 2; + const u32 clkgenenb_mask = 1ul << clkgenenb_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcclkenb; + read_value &= ~clkgenenb_mask; + read_value |= (u32)enable << clkgenenb_pos; + + writel(read_value, &pregister->dpcclkenb); +} + +int nx_dpc_get_clock_divisor_enable(u32 module_index) +{ + const u32 clkgenenb_pos = 2; + const u32 clkgenenb_mask = 1ul << clkgenenb_pos; + + return (int)((__g_module_variables[module_index].pregister->dpcclkenb & + clkgenenb_mask) >> clkgenenb_pos); +} + +void nx_dpc_set_dpc_enable(u32 module_index, int benb) +{ + const u32 intpend_pos = 10; + const u32 intpend_mask = 1ul << intpend_pos; + const u32 dpcenb_pos = 15; + const u32 dpcenb_mask = 1ul << dpcenb_pos; + register struct nx_dpc_register_set *pregister; + register u32 read_value; + + pregister = __g_module_variables[module_index].pregister; + read_value = pregister->dpcctrl0; + read_value &= ~(intpend_mask | dpcenb_mask); + read_value |= (u32)benb << dpcenb_pos; + + writel(read_value, &pregister->dpcctrl0); +} + +int nx_dpc_get_dpc_enable(u32 module_index) +{ + const u32 dpcenb_pos = 15; + const u32 dpcenb_mask = 1ul << dpcenb_pos; + + return (int)((__g_module_variables[module_index].pregister->dpcctrl0 & + dpcenb_mask) >> dpcenb_pos); +} + +void nx_dpc_set_delay(u32 module_index, u32 delay_rgb_pvd, u32 delay_hs_cp1, + u32 delay_vs_fram, u32 delay_de_cp2) +{ + const u32 intpend_mask = 1u << 10; + const u32 delayrgb_pos = 4; + const u32 delayrgb_mask = 0xfu << delayrgb_pos; + register u32 temp; + const u32 delayde_pos = 0; + const u32 delayvs_pos = 8; + const u32 delayhs_pos = 0; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + temp = pregister->dpcctrl0; + temp &= (u32)~(intpend_mask | delayrgb_mask); + temp = (u32)(temp | (delay_rgb_pvd << delayrgb_pos)); + + writel(temp, &pregister->dpcctrl0); + + writel((u32)((delay_vs_fram << delayvs_pos) | + (delay_hs_cp1 << delayhs_pos)), &pregister->dpcdelay0); + + writel((u32)(delay_de_cp2 << delayde_pos), &pregister->dpcdelay1); +} + +void nx_dpc_get_delay(u32 module_index, u32 *pdelayrgb_pvd, u32 *pdelayhs_cp1, + u32 *pdelayvs_fram, u32 *pdelayde_cp2) +{ + const u32 delayrgb_pos = 4; + const u32 delayrgb_mask = 0xfu << delayrgb_pos; + const u32 delayde_pos = 0; + const u32 delayde_mask = 0x3fu << delayde_pos; + const u32 delayvs_pos = 8; + const u32 delayvs_mask = 0x3fu << delayvs_pos; + const u32 delayhs_pos = 0; + const u32 delayhs_mask = 0x3fu << delayhs_pos; + register u32 temp; + + temp = __g_module_variables[module_index].pregister->dpcctrl0; + if (pdelayrgb_pvd) + *pdelayrgb_pvd = (u32)((temp & delayrgb_mask) >> delayrgb_pos); + temp = __g_module_variables[module_index].pregister->dpcdelay0; + if (pdelayhs_cp1) + *pdelayhs_cp1 = (u32)((temp & delayhs_mask) >> delayhs_pos); + if (pdelayvs_fram) + *pdelayvs_fram = (u32)((temp & delayvs_mask) >> delayvs_pos); + temp = __g_module_variables[module_index].pregister->dpcdelay1; + if (pdelayde_cp2) + *pdelayde_cp2 = (u32)((temp & delayde_mask) >> delayde_pos); +} + +void nx_dpc_set_dither(u32 module_index, enum nx_dpc_dither dither_r, + enum nx_dpc_dither dither_g, enum nx_dpc_dither dither_b) +{ + const u32 dither_mask = 0x3fu; + const u32 rdither_pos = 0; + const u32 gdither_pos = 2; + const u32 bdither_pos = 4; + register u32 temp; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + temp = pregister->dpcctrl1; + temp &= (u32)~dither_mask; + temp = (u32)(temp | + ((dither_b << bdither_pos) | (dither_g << gdither_pos) | + (dither_r << rdither_pos))); + + writel(temp, &pregister->dpcctrl1); +} + +void nx_dpc_get_dither(u32 module_index, enum nx_dpc_dither *pditherr, + enum nx_dpc_dither *pditherg, + enum nx_dpc_dither *pditherb) +{ + const u32 rdither_pos = 0; + const u32 rdither_mask = 0x3u << rdither_pos; + const u32 gdither_pos = 2; + const u32 gdither_mask = 0x3u << gdither_pos; + const u32 bdither_pos = 4; + const u32 bdither_mask = 0x3u << bdither_pos; + register u32 temp; + + temp = __g_module_variables[module_index].pregister->dpcctrl1; + if (pditherr) + *pditherr = + (enum nx_dpc_dither)((temp & rdither_mask) >> rdither_pos); + if (pditherg) + *pditherg = + (enum nx_dpc_dither)((temp & gdither_mask) >> gdither_pos); + if (pditherb) + *pditherb = + (enum nx_dpc_dither)((temp & bdither_mask) >> bdither_pos); +} + +void nx_dpc_set_mode(u32 module_index, enum nx_dpc_format format, + int binterlace, int binvertfield, int brgbmode, + int bswaprb, enum nx_dpc_ycorder ycorder, int bclipyc, + int bembeddedsync, enum nx_dpc_padclk clock, + int binvertclock, int bdualview) +{ + const u32 polfield_pos = 2; + const u32 seavenb_pos = 8; + const u32 scanmode_pos = 9; + const u32 intpend_pos = 10; + const u32 rgbmode_pos = 12; + + const u32 dither_mask = 0x3f; + const u32 ycorder_pos = 6; + const u32 format_pos = 8; + const u32 ycrange_pos = 13; + const u32 swaprb_pos = 15; + + const u32 padclksel_pos = 0; + const u32 padclksel_mask = 3u << padclksel_pos; + const u32 lcdtype_pos = 7; + const u32 lcdtype_mask = 3u << lcdtype_pos; + register struct nx_dpc_register_set *pregister; + register u32 temp; + + pregister = __g_module_variables[module_index].pregister; + temp = pregister->dpcctrl0; + temp &= (u32)~(1u << intpend_pos); + if (binterlace) + temp |= (u32)(1u << scanmode_pos); + else + temp &= (u32)~(1u << scanmode_pos); + if (binvertfield) + temp |= (u32)(1u << polfield_pos); + else + temp &= (u32)~(1u << polfield_pos); + if (brgbmode) + temp |= (u32)(1u << rgbmode_pos); + else + temp &= (u32)~(1u << rgbmode_pos); + if (bembeddedsync) + temp |= (u32)(1u << seavenb_pos); + else + temp &= (u32)~(1u << seavenb_pos); + + writel(temp, &pregister->dpcctrl0); + temp = pregister->dpcctrl1; + temp &= (u32)dither_mask; + temp = (u32)(temp | (ycorder << ycorder_pos)); + if (format >= 16) { + register u32 temp1; + + temp1 = pregister->dpcctrl2; + temp1 = temp1 | (1 << 4); + writel(temp1, &pregister->dpcctrl2); + } else { + register u32 temp1; + + temp1 = pregister->dpcctrl2; + temp1 = temp1 & ~(1 << 4); + writel(temp1, &pregister->dpcctrl2); + } + temp = (u32)(temp | ((format & 0xf) << format_pos)); + if (!bclipyc) + temp |= (u32)(1u << ycrange_pos); + if (bswaprb) + temp |= (u32)(1u << swaprb_pos); + + writel(temp, &pregister->dpcctrl1); + temp = pregister->dpcctrl2; + temp &= (u32)~(padclksel_mask | lcdtype_mask); + temp = (u32)(temp | (clock << padclksel_pos)); + + writel(temp, &pregister->dpcctrl2); + + nx_dpc_set_clock_out_inv(module_index, 0, binvertclock); + nx_dpc_set_clock_out_inv(module_index, 1, binvertclock); +} + +void nx_dpc_get_mode(u32 module_index, enum nx_dpc_format *pformat, + int *pbinterlace, int *pbinvertfield, int *pbrgbmode, + int *pbswaprb, enum nx_dpc_ycorder *pycorder, + int *pbclipyc, int *pbembeddedsync, + enum nx_dpc_padclk *pclock, int *pbinvertclock, + int *pbdualview) +{ + const u32 polfield = 1u << 2; + const u32 seavenb = 1u << 8; + const u32 scanmode = 1u << 9; + const u32 rgbmode = 1u << 12; + + const u32 ycorder_pos = 6; + const u32 ycorder_mask = 0x3u << ycorder_pos; + const u32 format_pos = 8; + const u32 format_mask = 0xfu << format_pos; + const u32 ycrange = 1u << 13; + const u32 swaprb = 1u << 15; + + const u32 padclksel_pos = 0; + const u32 padclksel_mask = 3u << padclksel_pos; + const u32 lcdtype_pos = 7; + const u32 lcdtype_mask = 3u << lcdtype_pos; + register u32 temp; + + temp = __g_module_variables[module_index].pregister->dpcctrl0; + if (pbinterlace) + *pbinterlace = (temp & scanmode) ? 1 : 0; + + if (pbinvertfield) + *pbinvertfield = (temp & polfield) ? 1 : 0; + + if (pbrgbmode) + *pbrgbmode = (temp & rgbmode) ? 1 : 0; + + if (pbembeddedsync) + *pbembeddedsync = (temp & seavenb) ? 1 : 0; + + temp = __g_module_variables[module_index].pregister->dpcctrl1; + + if (pycorder) + *pycorder = + (enum nx_dpc_ycorder)((temp & ycorder_mask) >> ycorder_pos); + + if (pformat) + *pformat = + (enum nx_dpc_format)((temp & format_mask) >> format_pos); + if (pbclipyc) + *pbclipyc = (temp & ycrange) ? 0 : 1; + if (pbswaprb) + *pbswaprb = (temp & swaprb) ? 1 : 0; + + temp = __g_module_variables[module_index].pregister->dpcctrl2; + + if (pclock) + *pclock = + (enum nx_dpc_padclk)((temp & padclksel_mask) >> + padclksel_pos); + + if (pbdualview) + *pbdualview = (2 == ((temp & lcdtype_mask) >> lcdtype_pos)) + ? 1 : 0; + + if (pbinvertclock) + *pbinvertclock = nx_dpc_get_clock_out_inv(module_index, 1); +} + +void nx_dpc_set_hsync(u32 module_index, u32 avwidth, u32 hsw, u32 hfp, u32 hbp, + int binvhsync) +{ + const u32 intpend = 1u << 10; + const u32 polhsync = 1u << 0; + register u32 temp; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + writel((u32)(hsw + hbp + avwidth + hfp - 1), &pregister->dpchtotal); + + writel((u32)(hsw - 1), &pregister->dpchswidth); + + writel((u32)(hsw + hbp - 1), &pregister->dpchastart); + + writel((u32)(hsw + hbp + avwidth - 1), &pregister->dpchaend); + temp = pregister->dpcctrl0; + temp &= ~intpend; + if (binvhsync) + temp |= (u32)polhsync; + else + temp &= (u32)~polhsync; + + writel(temp, &pregister->dpcctrl0); +} + +void nx_dpc_get_hsync(u32 module_index, u32 *pavwidth, u32 *phsw, u32 *phfp, + u32 *phbp, int *pbinvhsync) +{ + const u32 polhsync = 1u << 0; + u32 htotal, hsw, hab, hae; + u32 avw, hfp, hbp; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + htotal = (u32)pregister->dpchtotal + 1; + hsw = (u32)pregister->dpchswidth + 1; + hab = (u32)pregister->dpchastart + 1; + hae = (u32)pregister->dpchaend + 1; + hbp = hab - hsw; + avw = hae - hab; + hfp = htotal - hae; + if (pavwidth) + *pavwidth = avw; + if (phsw) + *phsw = hsw; + if (phfp) + *phfp = hfp; + if (phbp) + *phbp = hbp; + if (pbinvhsync) + *pbinvhsync = (pregister->dpcctrl0 & polhsync) ? 1 : 0; +} + +void nx_dpc_set_vsync(u32 module_index, u32 avheight, u32 vsw, u32 vfp, u32 vbp, + int binvvsync, u32 eavheight, u32 evsw, u32 evfp, + u32 evbp) +{ + const u32 intpend = 1u << 10; + const u32 polvsync = 1u << 1; + register u32 temp; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + writel((u32)(vsw + vbp + avheight + vfp - 1), &pregister->dpcvtotal); + + writel((u32)(vsw - 1), &pregister->dpcvswidth); + + writel((u32)(vsw + vbp - 1), &pregister->dpcvastart); + + writel((u32)(vsw + vbp + avheight - 1), &pregister->dpcvaend); + + writel((u32)(evsw + evbp + eavheight + evfp - 1), + &pregister->dpcevtotal); + + writel((u32)(evsw - 1), &pregister->dpcevswidth); + + writel((u32)(evsw + evbp - 1), &pregister->dpcevastart); + + writel((u32)(evsw + evbp + eavheight - 1), &pregister->dpcevaend); + temp = pregister->dpcctrl0; + temp &= ~intpend; + if (binvvsync) + temp |= (u32)polvsync; + else + temp &= (u32)~polvsync; + + writel(temp, &pregister->dpcctrl0); +} + +void nx_dpc_get_vsync(u32 module_index, u32 *pavheight, u32 *pvsw, u32 *pvfp, + u32 *pvbp, int *pbinvvsync, u32 *peavheight, + u32 *pevsw, u32 *pevfp, u32 *pevbp) +{ + const u32 polvsync = 1u << 1; + u32 vtotal, vsw, vab, vae; + u32 avh, vfp, vbp; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + vtotal = (u32)pregister->dpcvtotal + 1; + vsw = (u32)pregister->dpcvswidth + 1; + vab = (u32)pregister->dpcvastart + 1; + vae = (u32)pregister->dpcvaend + 1; + vbp = vab - vsw; + avh = vae - vab; + vfp = vtotal - vae; + if (pavheight) + *pavheight = avh; + if (pvsw) + *pvsw = vsw; + if (pvfp) + *pvfp = vfp; + if (pvbp) + *pvbp = vbp; + vtotal = (u32)pregister->dpcevtotal + 1; + vsw = (u32)pregister->dpcevswidth + 1; + vab = (u32)pregister->dpcevastart + 1; + vae = (u32)pregister->dpcevaend + 1; + vbp = vab - vsw; + avh = vae - vab; + vfp = vtotal - vae; + if (peavheight) + *peavheight = avh; + if (pevsw) + *pevsw = vsw; + if (pevfp) + *pevfp = vfp; + if (pevbp) + *pevbp = vbp; + if (pbinvvsync) + *pbinvvsync = (pregister->dpcctrl0 & polvsync) ? 1 : 0; +} + +void nx_dpc_set_vsync_offset(u32 module_index, u32 vssoffset, u32 vseoffset, + u32 evssoffset, u32 evseoffset) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + writel((u32)vseoffset, &pregister->dpcvseoffset); + + writel((u32)vssoffset, &pregister->dpcvssoffset); + + writel((u32)evseoffset, &pregister->dpcevseoffset); + + writel((u32)evssoffset, &pregister->dpcevssoffset); +} + +void nx_dpc_get_vsync_offset(u32 module_index, u32 *pvssoffset, + u32 *pvseoffset, u32 *pevssoffset, + u32 *pevseoffset) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + if (pvseoffset) + *pvseoffset = (u32)pregister->dpcvseoffset; + + if (pvssoffset) + *pvssoffset = (u32)pregister->dpcvssoffset; + + if (pevseoffset) + *pevseoffset = (u32)pregister->dpcevseoffset; + + if (pevssoffset) + *pevssoffset = (u32)pregister->dpcevssoffset; +} + +void nx_dpc_set_horizontal_up_scaler(u32 module_index, int benb, + u32 sourcewidth, u32 destwidth) +{ + const u32 upscalel_pos = 8; + const u32 upscaleh_pos = 0; + const u32 upscaleh_mask = ((1 << 15) - 1) << upscaleh_pos; + const u32 upscalerenb_pos = 0; + register struct nx_dpc_register_set *pregister; + register u32 regvalue; + register u32 up_scale; + + pregister = __g_module_variables[module_index].pregister; + up_scale = ((sourcewidth - 1) * (1 << 11)) / (destwidth - 1); + regvalue = 0; + regvalue |= (((u32)benb << upscalerenb_pos) | + (up_scale & 0xff) << upscalel_pos); + + writel(regvalue, &pregister->dpcupscalecon0); + + writel((up_scale >> 0x08) & upscaleh_mask, &pregister->dpcupscalecon1); + + writel(sourcewidth - 1, &pregister->dpcupscalecon2); +} + +void nx_dpc_get_horizontal_up_scaler(u32 module_index, int *pbenb, + u32 *psourcewidth, u32 *pdestwidth) +{ + const u32 upscalerenb_pos = 0; + const u32 upscalerenb_mask = 1u << upscalerenb_pos; + register struct nx_dpc_register_set *pregister; + + u32 up_scale; + u32 destwidth, srcwidth; + + pregister = __g_module_variables[module_index].pregister; + up_scale = ((u32)(pregister->dpcupscalecon1 & 0x7fff) << 8) | + ((u32)(pregister->dpcupscalecon0 >> 8) & 0xff); + srcwidth = pregister->dpcupscalecon2; + destwidth = (srcwidth * (1 << 11)) / up_scale; + if (pbenb) + *pbenb = (pregister->dpcupscalecon0 & upscalerenb_mask); + if (psourcewidth) + *psourcewidth = srcwidth + 1; + if (pdestwidth) + *pdestwidth = destwidth + 1; +} + +void nx_dpc_set_sync(u32 module_index, enum syncgenmode sync_gen_mode, + u32 avwidth, u32 avheight, u32 hsw, u32 hfp, u32 hbp, + u32 vsw, u32 vfp, u32 vbp, enum polarity field_polarity, + enum polarity hsyncpolarity, enum polarity vsyncpolarity, + u32 even_vsw, u32 even_vfp, u32 even_vbp, u32 vsetpixel, + u32 vsclrpixel, u32 evenvsetpixel, u32 evenvsclrpixel) +{ + register struct nx_dpc_register_set *pregister; + u32 regvalue = 0; + + pregister = __g_module_variables[module_index].pregister; + + writel((u32)(hfp + hsw + hbp + avwidth - 1), &pregister->dpchtotal); + writel((u32)(hsw - 1), &pregister->dpchswidth); + writel((u32)(hsw + hbp - 1), &pregister->dpchastart); + writel((u32)(hsw + hbp + avwidth - 1), &pregister->dpchaend); + writel((u32)(vfp + vsw + vbp + avheight - 1), &pregister->dpcvtotal); + writel((u32)(vsw - 1), &pregister->dpcvswidth); + writel((u32)(vsw + vbp - 1), &pregister->dpcvastart); + writel((u32)(vsw + vbp + avheight - 1), &pregister->dpcvaend); + writel((u32)vsetpixel, &pregister->dpcvseoffset); + writel((u32)(hfp + hsw + hbp + avwidth - vsclrpixel - 1), + &pregister->dpcvssoffset); + writel((u32)evenvsetpixel, &pregister->dpcevseoffset); + writel((u32)(hfp + hsw + hbp + avwidth - evenvsclrpixel - 1), + &pregister->dpcevssoffset); + if (sync_gen_mode == 1) { + writel((u32)(even_vfp + even_vsw + even_vbp + avheight - 1), + &pregister->dpcevtotal); + writel((u32)(even_vsw - 1), &pregister->dpcevswidth); + writel((u32)(even_vsw + even_vbp - 1), + &pregister->dpcevastart); + writel((u32)(even_vsw + even_vbp + avheight - 1), + &pregister->dpcevaend); + } + regvalue = readl(&pregister->dpcctrl0) & 0xfff0ul; + regvalue |= (((u32)field_polarity << 2) | ((u32)vsyncpolarity << 1) | + ((u32)hsyncpolarity << 0)); + writel((u32)regvalue, &pregister->dpcctrl0); +} + +void nx_dpc_set_output_format(u32 module_index, enum outputformat output_format, + u8 output_video_config) +{ + const u32 format_table[] = { + (0 << 0), (1 << 0), (2 << 0), (3 << 0), (4 << 0), (5 << 0), + (6 << 0), (7 << 0), (8 << 0), (9 << 0), (0 << 0) | (1 << 7), + (1 << 0) | (1 << 7), (2 << 0) | (1 << 7), (3 << 0) | (1 << 7), + (4 << 0) | (1 << 7), (5 << 0) | (1 << 7), (6 << 0) | (1 << 7), + (7 << 0) | (1 << 7), (8 << 0) | (1 << 7), (9 << 0) | (1 << 7), + (10 << 0), (11 << 0), (12 << 0), (13 << 0), (14 << 0), (15 << 0) + }; + u32 regvalue; + u32 regvalue0; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + regvalue = readl(&pregister->dpcctrl1) & 0x30fful; + + regvalue |= (format_table[output_format] << 8); + writel((u32)regvalue, &pregister->dpcctrl1); + regvalue0 = (u32)(readl(&pregister->dpcctrl1) & 0xff3f); + regvalue0 = (u32)((output_video_config << 6) | regvalue0); + writel((u32)regvalue0, &pregister->dpcctrl1); +} + +void nx_dpc_set_quantization_mode(u32 module_index, enum qmode rgb2yc, + enum qmode yc2rgb) +{ + register struct nx_dpc_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + regvalue = readl(&pregister->dpcctrl1) & 0x8ffful; + regvalue |= ((u32)rgb2yc << 13) | ((u32)yc2rgb << 12); + writel((u32)regvalue, &pregister->dpcctrl1); +} + +void nx_dpc_set_enable(u32 module_index, int enable, int rgbmode, + int use_ntscsync, int use_analog_output, int seavenable) +{ + u32 regvalue; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + regvalue = readl(&pregister->dpcctrl0) & 0x0efful; + regvalue |= ((u32)enable << 15) | ((u32)use_ntscsync << 14) | + ((u32)seavenable << 8) | ((u32)use_analog_output << 13) | + ((u32)rgbmode << 12); + writel((u32)regvalue, &pregister->dpcctrl0); +} + +void nx_dpc_set_out_video_clk_select(u32 module_index, + enum outpadclksel out_pad_vclk_sel) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + writel((u32)((readl(&pregister->dpcctrl2)) | (out_pad_vclk_sel & 0x3)), + &pregister->dpcctrl2); +} + +void nx_dpc_set_reg_flush(u32 module_index) +{ + u32 reg; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + reg = readl(&pregister->dpcdataflush); + writel((u32)(reg | (1ul << 4)), &pregister->dpcdataflush); +} + +void nx_dpc_set_sramon(u32 module_index) +{ + u32 reg; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + reg = (u32)(readl(&pregister->dpcctrl2) & 0xf3ff); + writel((u32)(reg | (1ul << 10)), &pregister->dpcctrl2); + reg = (u32)(readl(&pregister->dpcctrl2) & 0xf7ff); + writel((u32)(reg | (1ul << 11)), &pregister->dpcctrl2); +} + +void nx_dpc_set_sync_lcdtype(u32 module_index, int stnlcd, int dual_view_enb, + int bit_widh, u8 cpcycle) +{ + u32 reg; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + + reg = (u32)(readl(&pregister->dpcctrl2) & 0xc0f); + writel((u32)(reg | (cpcycle << 12) | (bit_widh << 9) | + (dual_view_enb << 8) | (stnlcd << 7)), + &pregister->dpcctrl2); +} + +void nx_dpc_set_up_scale_control(u32 module_index, int up_scale_enb, + int filter_enb, u32 hscale, u16 source_width) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u32)((hscale << 8) | ((u32)filter_enb << 1) | (up_scale_enb)), + &pregister->dpcupscalecon0); + writel((u32)(hscale >> 8), &pregister->dpcupscalecon1); + writel(source_width, &pregister->dpcupscalecon2); +} + +void nx_dpc_set_mputime(u32 module_index, u8 setup, u8 hold, u8 acc) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u32)((setup << 8) | (hold & 0xff)), &pregister->dpcmputime0); + writel((u32)(acc), &pregister->dpcmputime1); +} + +void nx_dpc_set_index(u32 module_index, u32 index) +{ + u32 regvalue; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u32)(index & 0xffff), &pregister->dpcmpuwrdatal); + writel((u32)((index >> 16) & 0xff), &pregister->dpcmpuindex); + if (index == 0x22) { + regvalue = readl(&pregister->dpcctrl2); + writel((regvalue | 0x10), &pregister->dpcctrl2); + } +} + +void nx_dpc_set_data(u32 module_index, u32 data) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u32)(data & 0xffff), &pregister->dpcmpuwrdatal); + writel((u32)((data >> 16) & 0xff), &pregister->dpcmpudatah); +} + +void nx_dpc_set_cmd_buffer_flush(u32 module_index) +{ + u32 reg; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + reg = readl(&pregister->dpcdataflush); + writel((u32)(reg | (1 << 1)), &pregister->dpcdataflush); +} + +void nx_dpc_set_cmd_buffer_clear(u32 module_index) +{ + u32 reg; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + reg = readl(&pregister->dpcdataflush); + writel((u32)(reg | (1 << 0)), &pregister->dpcdataflush); +} + +void nx_dpc_set_cmd_buffer_write(u32 module_index, u32 cmd_data) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u32)(cmd_data & 0xffff), &pregister->dpccmdbufferdatal); + writel((u32)(cmd_data >> 16), &pregister->dpccmdbufferdatah); +} + +void nx_dpc_set(u32 module_index) +{ + u32 reg; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + reg = readl(&pregister->dpcpolctrl); + writel((u32)(reg | 0x1), &pregister->dpcpolctrl); +} + +u32 nx_dpc_get_data(u32 module_index) +{ + u32 reg = 0; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + reg = readl(&pregister->dpcmpudatah); + reg = (reg << 16) | readl(&pregister->dpcmpurdatal); + return reg; +} + +u32 nx_dpc_get_status(u32 module_index) +{ + u32 reg = 0; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + reg = readl(&pregister->dpcmpustatus); + reg = (reg << 16) | readl(&pregister->dpcmpurdatal); + return reg; +} + +void nx_dpc_rgbmask(u32 module_index, u32 rgbmask) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((rgbmask >> 0) & 0xffff, &pregister->dpcrgbmask[0]); + writel((rgbmask >> 16) & 0x00ff, &pregister->dpcrgbmask[1]); +} + +void nx_dpc_set_pad_location(u32 module_index, u32 index, u32 regvalue) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(regvalue, &pregister->dpcpadposition[index]); +} + +u32 nx_dpc_get_field_flag(u32 module_index) +{ + register struct nx_dpc_register_set *pregister; + u32 regvalue; + + pregister = __g_module_variables[module_index].pregister; + regvalue = readl(&pregister->dpcrgbshift); + + return (u32)((regvalue >> 5) & 0x01); +} + +void nx_dpc_set_enable_with_interlace(u32 module_index, int enable, int rgbmode, + int use_ntscsync, int use_analog_output, + int seavenable) +{ + u32 regvalue; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + regvalue = readl(&pregister->dpcctrl0) & 0x0eff; + regvalue = readl(&pregister->dpcctrl0) & 0x0eff; + regvalue |= ((u32)enable << 15) | ((u32)use_ntscsync << 14) | + ((u32)seavenable << 8) | ((u32)use_analog_output << 13) | + ((u32)rgbmode << 12); + + regvalue |= (1 << 9); + writel((u16)regvalue, &pregister->dpcctrl0); +} + +void nx_dpc_set_encoder_control_reg(u32 module_index, u32 param_a, u32 param_b, + u32 param_c) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(param_a, &pregister->ntsc_ecmda); + writel(param_b, &pregister->ntsc_ecmdb); + writel(param_c, &pregister->ntsc_ecmdc); +} + +void nx_dpc_set_encoder_shcphase_control(u32 module_index, u32 chroma_param) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(chroma_param, &pregister->ntsc_sch); +} + +void nx_dpc_set_encoder_timing_config_reg(u32 module_index, u32 icntl) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(icntl, &pregister->ntsc_icntl); +} + +void nx_dpc_set_encoder_dacoutput_select(u32 module_index, u8 dacsel0, + u8 dacsel1, u8 dacsel2, u8 dacsel3, + u8 dacsel4, u8 dacsel5) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(((dacsel1 & 0xf) << 4) | (dacsel0 & 0xf), + &pregister->ntsc_dacsel10); + writel(((dacsel3 & 0xf) << 4) | (dacsel2 & 0xf), + &pregister->ntsc_dacsel32); + writel(((dacsel5 & 0xf) << 4) | (dacsel4 & 0xf), + &pregister->ntsc_dacsel54); +} + +void nx_dpc_set_encoder_sync_location(u32 module_index, u16 hsoe, u16 hsob, + u16 vsob, u16 vsoe, u8 vsost, int novrst) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u16)((((vsob & 0x100) >> 2) | ((hsob & 0x700) >> 5) | + (hsoe & 0x700) >> 8)), &pregister->ntsc_hsvso); + writel((u16)(hsoe & 0xff), &pregister->ntsc_hsoe); + writel((u16)(hsob & 0xff), &pregister->ntsc_hsob); + writel((u16)(vsob & 0xff), &pregister->ntsc_vsob); + writel((u16)(((vsost & 0x3) << 6) | (novrst << 5) | (vsoe & 0x1f)), + &pregister->ntsc_vsoe); +} + +void nx_dpc_set_encoder_dacpower_enable(u32 module_index, u8 dacpd) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(dacpd, &pregister->ntsc_dacpd); +} + +void nx_dpc_set_ycorder(u32 module_index, enum nx_dpc_ycorder ycorder) +{ + const u16 ycorder_pos = 6; + register struct nx_dpc_register_set *pregister; + u32 temp; + + pregister = __g_module_variables[module_index].pregister; + temp = pregister->dpcctrl1 & (~(0xf << ycorder_pos)); + temp = (u16)(temp | (ycorder << ycorder_pos)); + writel(temp, &pregister->dpcctrl1); +} + +void nx_dpc_set_luma_gain(u32 module_index, u32 luma_gain) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel(luma_gain, &pregister->ntsc_cont); +} + +void nx_dpc_set_encenable(u32 module_index, int benb) +{ + const u16 encmode = 1u << 14; + const u16 encrst = 1u << 13; + const u16 intpend = 1u << 10; + register struct nx_dpc_register_set *pregister; + register u16 temp; + + pregister = __g_module_variables[module_index].pregister; + temp = readl(&pregister->dpcctrl0); + temp &= (u16)~intpend; + if (benb) + temp |= (u16)encrst; + else + temp &= (u16)~encrst; + writel((temp | encmode), &pregister->dpcctrl0); + writel(7, &pregister->ntsc_icntl); +} + +int nx_dpc_get_encenable(u32 module_index) +{ + const u16 encrst = 1u << 13; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return (readl(&pregister->dpcctrl0) & encrst) ? 1 : 0; +} + +void nx_dpc_set_video_encoder_power_down(u32 module_index, int benb) +{ + const u16 pwdenc = 1u << 7; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (benb) { + writel(readl(&pregister->ntsc_ecmda) | (u16)pwdenc, + &pregister->ntsc_ecmda); + writel(0, &pregister->ntsc_dacsel10); + } else { + writel(1, &pregister->ntsc_dacsel10); + writel(readl(&pregister->ntsc_ecmda) & (u16)~pwdenc, + &pregister->ntsc_ecmda); + } +} + +int nx_dpc_get_video_encoder_power_down(u32 module_index) +{ + const u16 pwdenc = 1u << 7; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return (readl(&pregister->ntsc_ecmda) & pwdenc) ? 1 : 0; +} + +void nx_dpc_set_video_encoder_mode(u32 module_index, enum nx_dpc_vbs vbs, + int bpedestal) +{ + register struct nx_dpc_register_set *pregister; + +#define phalt (1u << 0) +#define ifmt (1u << 1) +#define ped (1u << 3) +#define fscsel_ntsc (0u << 4) +#define fscsel_pal (1u << 4) +#define fscsel_palm (2u << 4) +#define fscsel_paln (3u << 4) +#define fdrst (1u << 6) +#define pwdenc (1u << 7) + register u16 temp; + static const u8 ntsc_ecmda_table[] = { + (u8)(fscsel_ntsc | fdrst), (u8)(ifmt | fscsel_ntsc), + (u8)(fscsel_pal), (u8)(fscsel_palm | phalt), + (u8)(ifmt | fscsel_paln | phalt), + (u8)(ifmt | fscsel_pal | phalt | fdrst), + (u8)(fscsel_pal | phalt), + (u8)(ifmt | fscsel_ntsc) + }; + pregister = __g_module_variables[module_index].pregister; + temp = readl(&pregister->ntsc_ecmda); + temp &= (u16)pwdenc; + temp = (u16)(temp | (u16)ntsc_ecmda_table[vbs]); + if (bpedestal) + temp |= (u16)ped; + writel(temp, &pregister->ntsc_ecmda); +#undef phalt +#undef ifmt +#undef ped +#undef fscsel_ntsc +#undef fscsel_pal +#undef fscsel_palm +#undef fscsel_paln +#undef fdrst +#undef pwdenc +} + +void nx_dpc_set_video_encoder_schlock_control(u32 module_index, int bfreerun) +{ + const u16 fdrst = 1u << 6; + register struct nx_dpc_register_set *pregister; + register u16 temp; + + pregister = __g_module_variables[module_index].pregister; + temp = readl(&pregister->ntsc_ecmda); + if (bfreerun) + temp |= (u16)fdrst; + else + temp &= (u16)~fdrst; + writel(temp, &pregister->ntsc_ecmda); +} + +int nx_dpc_get_video_encoder_schlock_control(u32 module_index) +{ + const u16 fdrst = 1u << 6; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + return (readl(&pregister->ntsc_ecmda) & fdrst) ? 1 : 0; +} + +void nx_dpc_set_video_encoder_bandwidth(u32 module_index, + enum nx_dpc_bandwidth luma, + enum nx_dpc_bandwidth chroma) +{ + const u16 ybw_pos = 0; + const u16 cbw_pos = 2; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u16)((chroma << cbw_pos) | (luma << ybw_pos)), + &pregister->ntsc_ecmdb); +} + +void nx_dpc_get_video_encoder_bandwidth(u32 module_index, + enum nx_dpc_bandwidth *pluma, + enum nx_dpc_bandwidth *pchroma) +{ + const u16 ybw_pos = 0; + const u16 ybw_mask = 3u << ybw_pos; + const u16 cbw_pos = 2; + const u16 cbw_mask = 3u << cbw_pos; + register struct nx_dpc_register_set *pregister; + register u16 temp; + + pregister = __g_module_variables[module_index].pregister; + temp = readl(&pregister->ntsc_ecmdb); + if (pluma) + *pluma = (enum nx_dpc_bandwidth)((temp & ybw_mask) >> ybw_pos); + if (pchroma) + *pchroma = + (enum nx_dpc_bandwidth)((temp & cbw_mask) >> cbw_pos); +} + +void nx_dpc_set_video_encoder_color_control(u32 module_index, s8 sch, + s8 hue, s8 sat, s8 crt, + s8 brt) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u16)sch, &pregister->ntsc_sch); + writel((u16)hue, &pregister->ntsc_hue); + writel((u16)sat, &pregister->ntsc_sat); + writel((u16)crt, &pregister->ntsc_cont); + writel((u16)brt, &pregister->ntsc_bright); +} + +void nx_dpc_get_video_encoder_color_control(u32 module_index, s8 *psch, + s8 *phue, s8 *psat, + s8 *pcrt, s8 *pbrt) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + if (psch) + *psch = (s8)readl(&pregister->ntsc_sch); + if (phue) + *phue = (s8)readl(&pregister->ntsc_hue); + if (psat) + *psat = (s8)readl(&pregister->ntsc_sat); + if (pcrt) + *pcrt = (s8)readl(&pregister->ntsc_cont); + if (pbrt) + *pbrt = (s8)readl(&pregister->ntsc_bright); +} + +void nx_dpc_set_video_encoder_fscadjust(u32 module_index, int16_t adjust) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + writel((u16)(adjust >> 8), &pregister->ntsc_fsc_adjh); + writel((u16)(adjust & 0xff), &pregister->ntsc_fsc_adjl); +} + +u16 nx_dpc_get_video_encoder_fscadjust(u32 module_index) +{ + register u32 temp; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + temp = (u32)readl(&pregister->ntsc_fsc_adjh); + temp <<= 8; + temp |= (((u32)readl(&pregister->ntsc_fsc_adjl)) & 0xff); + return (u16)temp; +} + +void nx_dpc_set_video_encoder_timing(u32 module_index, u32 hsos, u32 hsoe, + u32 vsos, u32 vsoe) +{ + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + hsos -= 1; + hsoe -= 1; + writel((u16)((((vsos >> 8) & 1u) << 6) | (((hsos >> 8) & 7u) << 3) | + (((hsoe >> 8) & 7u) << 0)), &pregister->ntsc_hsvso); + writel((u16)(hsos & 0xffu), &pregister->ntsc_hsob); + writel((u16)(hsoe & 0xffu), &pregister->ntsc_hsoe); + writel((u16)(vsos & 0xffu), &pregister->ntsc_vsob); + writel((u16)(vsoe & 0x1fu), &pregister->ntsc_vsoe); +} + +void nx_dpc_get_video_encoder_timing(u32 module_index, u32 *phsos, u32 *phsoe, + u32 *pvsos, u32 *pvsoe) +{ + register u16 hsvso; + register struct nx_dpc_register_set *pregister; + + pregister = __g_module_variables[module_index].pregister; + hsvso = readl(&pregister->ntsc_hsvso); + if (phsos) + *phsos = (u32)((((hsvso >> 3) & 7u) << 8) | + (readl(&pregister->ntsc_hsob) & 0xffu)) + 1; + if (phsoe) + *phsoe = (u32)((((hsvso >> 0) & 7u) << 8) | + (readl(&pregister->ntsc_hsoe) & 0xffu)) + 1; + if (pvsos) + *pvsos = (u32)((((hsvso >> 6) & 1u) << 8) | + (readl(&pregister->ntsc_vsob) & 0xffu)); + if (pvsoe) + *pvsoe = (u32)(readl(&pregister->ntsc_vsoe) & 0x1fu); +} diff --git a/drivers/video/nexell/soc/s5pxx18_soc_dpc.h b/drivers/video/nexell/soc/s5pxx18_soc_dpc.h new file mode 100644 index 0000000..cfa53c3 --- /dev/null +++ b/drivers/video/nexell/soc/s5pxx18_soc_dpc.h @@ -0,0 +1,444 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#ifndef _S5PXX18_SOC_DPC_H_ +#define _S5PXX18_SOC_DPC_H_ + +#include "s5pxx18_soc_disptype.h" + +#define IRQ_OFFSET 32 +#define IRQ_DPC_P (IRQ_OFFSET + 33) +#define IRQ_DPC_S (IRQ_OFFSET + 34) + +#define NUMBER_OF_DPC_MODULE 2 +#define PHY_BASEADDR_DPC0 0xC0102800 +#define PHY_BASEADDR_DPC1 0xC0102C00 + +#define PHY_BASEADDR_DPC_LIST \ + { PHY_BASEADDR_DPC0, PHY_BASEADDR_DPC1 } + +struct nx_dpc_register_set { + u32 ntsc_stata; + u32 ntsc_ecmda; + u32 ntsc_ecmdb; + u32 ntsc_glk; + u32 ntsc_sch; + u32 ntsc_hue; + u32 ntsc_sat; + u32 ntsc_cont; + u32 ntsc_bright; + u32 ntsc_fsc_adjh; + u32 ntsc_fsc_adjl; + u32 ntsc_ecmdc; + u32 ntsc_csdly; + u32 __ntsc_reserved_0_[3]; + u32 ntsc_dacsel10; + u32 ntsc_dacsel32; + u32 ntsc_dacsel54; + u32 ntsc_daclp; + u32 ntsc_dacpd; + u32 __ntsc_reserved_1_[(0x20 - 0x15)]; + u32 ntsc_icntl; + u32 ntsc_hvoffst; + u32 ntsc_hoffst; + u32 ntsc_voffset; + u32 ntsc_hsvso; + u32 ntsc_hsob; + u32 ntsc_hsoe; + u32 ntsc_vsob; + u32 ntsc_vsoe; + u32 __reserved[(0xf8 / 4) - 0x29]; + u32 dpchtotal; + u32 dpchswidth; + u32 dpchastart; + u32 dpchaend; + u32 dpcvtotal; + u32 dpcvswidth; + u32 dpcvastart; + u32 dpcvaend; + u32 dpcctrl0; + u32 dpcctrl1; + u32 dpcevtotal; + u32 dpcevswidth; + u32 dpcevastart; + u32 dpcevaend; + u32 dpcctrl2; + u32 dpcvseoffset; + u32 dpcvssoffset; + u32 dpcevseoffset; + u32 dpcevssoffset; + u32 dpcdelay0; + u32 dpcupscalecon0; + u32 dpcupscalecon1; + u32 dpcupscalecon2; + + u32 dpcrnumgencon0; + u32 dpcrnumgencon1; + u32 dpcrnumgencon2; + u32 dpcrndconformula_l; + u32 dpcrndconformula_h; + u32 dpcfdtaddr; + u32 dpcfrdithervalue; + u32 dpcfgdithervalue; + u32 dpcfbdithervalue; + u32 dpcdelay1; + u32 dpcmputime0; + u32 dpcmputime1; + u32 dpcmpuwrdatal; + u32 dpcmpuindex; + u32 dpcmpustatus; + u32 dpcmpudatah; + u32 dpcmpurdatal; + u32 dpcdummy12; + u32 dpccmdbufferdatal; + u32 dpccmdbufferdatah; + u32 dpcpolctrl; + u32 dpcpadposition[8]; + u32 dpcrgbmask[2]; + u32 dpcrgbshift; + u32 dpcdataflush; + u32 __reserved06[((0x3c0) - (2 * 0x0ec)) / 4]; + + u32 dpcclkenb; + u32 dpcclkgen[2][2]; +}; + +enum { + nx_dpc_int_vsync = 0 +}; + +enum nx_dpc_format { + nx_dpc_format_rgb555 = 0ul, + nx_dpc_format_rgb565 = 1ul, + nx_dpc_format_rgb666 = 2ul, + nx_dpc_format_rgb666b = 18ul, + nx_dpc_format_rgb888 = 3ul, + nx_dpc_format_mrgb555a = 4ul, + nx_dpc_format_mrgb555b = 5ul, + nx_dpc_format_mrgb565 = 6ul, + nx_dpc_format_mrgb666 = 7ul, + nx_dpc_format_mrgb888a = 8ul, + nx_dpc_format_mrgb888b = 9ul, + nx_dpc_format_ccir656 = 10ul, + nx_dpc_format_ccir601a = 12ul, + nx_dpc_format_ccir601b = 13ul, + nx_dpc_format_srgb888 = 14ul, + nx_dpc_format_srgbd8888 = 15ul, + nx_dpc_format_4096color = 1ul, + nx_dpc_format_16gray = 3ul +}; + +enum nx_dpc_ycorder { + nx_dpc_ycorder_cb_ycr_y = 0ul, + nx_dpc_ycorder_cr_ycb_y = 1ul, + nx_dpc_ycorder_ycbycr = 2ul, + nx_dpc_ycorder_ycrycb = 3ul +}; + +enum nx_dpc_padclk { + nx_dpc_padclk_vclk = 0ul, + nx_dpc_padclk_vclk2 = 1ul, + nx_dpc_padclk_vclk3 = 2ul +}; + +enum nx_dpc_dither { + nx_dpc_dither_bypass = 0ul, + nx_dpc_dither_4bit = 1ul, + nx_dpc_dither_5bit = 2ul, + nx_dpc_dither_6bit = 3ul +}; + +enum nx_dpc_vbs { + nx_dpc_vbs_ntsc_m = 0ul, + nx_dpc_vbs_ntsc_n = 1ul, + nx_dpc_vbs_ntsc_443 = 2ul, + nx_dpc_vbs_pal_m = 3ul, + nx_dpc_vbs_pal_n = 4ul, + nx_dpc_vbs_pal_bghi = 5ul, + nx_dpc_vbs_pseudo_pal = 6ul, + nx_dpc_vbs_pseudo_ntsc = 7ul +}; + +enum nx_dpc_bandwidth { + nx_dpc_bandwidth_low = 0ul, + nx_dpc_bandwidth_medium = 1ul, + nx_dpc_bandwidth_high = 2ul +}; + +int nx_dpc_initialize(void); +u32 nx_dpc_get_number_of_module(void); +u32 nx_dpc_get_physical_address(u32 module_index); +u32 nx_dpc_get_size_of_register_set(void); +void nx_dpc_set_base_address(u32 module_index, void *base_address); +void *nx_dpc_get_base_address(u32 module_index); +int nx_dpc_open_module(u32 module_index); +int nx_dpc_close_module(u32 module_index); +int nx_dpc_check_busy(u32 module_index); +int nx_dpc_can_power_down(u32 module_index); +int32_t nx_dpc_get_interrupt_number(u32 module_index); +void nx_dpc_set_interrupt_enable(u32 module_index, int32_t int_num, + int enable); +int nx_dpc_get_interrupt_enable(u32 module_index, int32_t int_num); +int nx_dpc_get_interrupt_pending(u32 module_index, int32_t int_num); +void nx_dpc_clear_interrupt_pending(u32 module_index, int32_t int_num); +void nx_dpc_set_interrupt_enable_all(u32 module_index, int enable); +int nx_dpc_get_interrupt_enable_all(u32 module_index); +int nx_dpc_get_interrupt_pending_all(u32 module_index); +void nx_dpc_clear_interrupt_pending_all(u32 module_index); +void nx_dpc_set_interrupt_enable32(u32 module_index, u32 enable_flag); +u32 nx_dpc_get_interrupt_enable32(u32 module_index); +u32 nx_dpc_get_interrupt_pending32(u32 module_index); +void nx_dpc_clear_interrupt_pending32(u32 module_index, + u32 pending_flag); +int32_t nx_dpc_get_interrupt_pending_number(u32 module_index); +void nx_dpc_set_clock_pclk_mode(u32 module_index, enum nx_pclkmode mode); +enum nx_pclkmode nx_dpc_get_clock_pclk_mode(u32 module_index); +void nx_dpc_set_clock_source(u32 module_index, u32 index, u32 clk_src); +u32 nx_dpc_get_clock_source(u32 module_index, u32 index); +void nx_dpc_set_clock_divisor(u32 module_index, u32 index, u32 divisor); +u32 nx_dpc_get_clock_divisor(u32 module_index, u32 index); +void nx_dpc_set_clock_out_inv(u32 module_index, u32 index, + int out_clk_inv); +int nx_dpc_get_clock_out_inv(u32 module_index, u32 index); +void nx_dpc_set_clock_out_select(u32 module_index, u32 index, + int bbypass); +int nx_dpc_get_clock_out_select(u32 module_index, u32 index); +void nx_dpc_set_clock_polarity(u32 module_index, int bpolarity); +int nx_dpc_get_clock_polarity(u32 module_index); +void nx_dpc_set_clock_out_enb(u32 module_index, u32 index, + int out_clk_enb); +int nx_dpc_get_clock_out_enb(u32 module_index, u32 index); +void nx_dpc_set_clock_out_delay(u32 module_index, u32 index, u32 delay); +u32 nx_dpc_get_clock_out_delay(u32 module_index, u32 index); +void nx_dpc_set_clock_divisor_enable(u32 module_index, int enable); +int nx_dpc_get_clock_divisor_enable(u32 module_index); + +void nx_dpc_set_dpc_enable(u32 module_index, int benb); +int nx_dpc_get_dpc_enable(u32 module_index); +void nx_dpc_set_delay(u32 module_index, u32 delay_rgb_pvd, + u32 delay_hs_cp1, u32 delay_vs_fram, + u32 delay_de_cp2); +void nx_dpc_get_delay(u32 module_index, u32 *pdelayrgb_pvd, + u32 *pdelayhs_cp1, u32 *pdelayvs_fram, + u32 *pdelayde_cp2); +void nx_dpc_set_dither(u32 module_index, enum nx_dpc_dither dither_r, + enum nx_dpc_dither dither_g, + enum nx_dpc_dither dither_b); +void nx_dpc_get_dither(u32 module_index, enum nx_dpc_dither *pditherr, + enum nx_dpc_dither *pditherg, + enum nx_dpc_dither *pditherb); +void nx_dpc_set_horizontal_up_scaler(u32 module_index, int benb, + u32 sourcewidth, u32 destwidth); +void nx_dpc_get_horizontal_up_scaler(u32 module_index, int *pbenb, + u32 *psourcewidth, + u32 *pdestwidth); + +void nx_dpc_set_mode(u32 module_index, enum nx_dpc_format format, + int binterlace, int binvertfield, int brgbmode, + int bswaprb, enum nx_dpc_ycorder ycorder, + int bclipyc, int bembeddedsync, + enum nx_dpc_padclk clock, int binvertclock, + int bdualview); +void nx_dpc_get_mode(u32 module_index, enum nx_dpc_format *pformat, + int *pbinterlace, int *pbinvertfield, + int *pbrgbmode, int *pbswaprb, + enum nx_dpc_ycorder *pycorder, int *pbclipyc, + int *pbembeddedsync, enum nx_dpc_padclk *pclock, + int *pbinvertclock, int *pbdualview); +void nx_dpc_set_hsync(u32 module_index, u32 avwidth, u32 hsw, u32 hfp, + u32 hbp, int binvhsync); +void nx_dpc_get_hsync(u32 module_index, u32 *pavwidth, u32 *phsw, + u32 *phfp, u32 *phbp, int *pbinvhsync); +void nx_dpc_set_vsync(u32 module_index, u32 avheight, u32 vsw, u32 vfp, + u32 vbp, int binvvsync, u32 eavheight, u32 evsw, + u32 evfp, u32 evbp); +void nx_dpc_get_vsync(u32 module_index, u32 *pavheight, u32 *pvsw, + u32 *pvfp, u32 *pvbp, int *pbinvvsync, + u32 *peavheight, u32 *pevsw, u32 *pevfp, + u32 *pevbp); +void nx_dpc_set_vsync_offset(u32 module_index, u32 vssoffset, + u32 vseoffset, u32 evssoffset, + u32 evseoffset); +void nx_dpc_get_vsync_offset(u32 module_index, u32 *pvssoffset, + u32 *pvseoffset, u32 *pevssoffset, + u32 *pevseoffset); + +u32 nx_dpc_enable_pad_tft(u32 module_index, u32 mode_index); +u32 nx_dpc_enable_pad_i80(u32 module_index, u32 mode_index); + +enum syncgenmode { + progressive = 0, + interlace = 1 +}; + +enum polarity { + polarity_activehigh = 0, + polarity_activelow = 1 +}; + +enum outputformat { + outputformat_rgb555 = 0, + outputformat_rgb565 = 1, + outputformat_rgb666 = 2, + outputformat_rgb888 = 3, + outputformat_mrgb555a = 4, + outputformat_mrgb555b = 5, + outputformat_mrgb565 = 6, + outputformat_mrgb666 = 7, + outputformat_mrgb888a = 8, + outputformat_mrgb888b = 9, + outputformat_bgr555 = 10, + outputformat_bgr565 = 11, + outputformat_bgr666 = 12, + outputformat_bgr888 = 13, + outputformat_mbgr555a = 14, + outputformat_mbgr555b = 15, + outputformat_mbgr565 = 16, + outputformat_mbgr666 = 17, + outputformat_mbgr888a = 18, + outputformat_mbgr888b = 19, + outputformat_ccir656 = 20, + outputformat_ccir601_8 = 21, + outputformat_ccir601_16a = 22, + outputformat_ccir601_16b = 23, + outputformat_srgb888 = 24, + outputformat_srgbd8888 = 25 +}; + +enum outpadclksel { + padvclk = 0, + padvclk2 = 1, + padvclk3 = 2 +}; + +enum qmode { + qmode_220 = 0, + qmode_256 = 1 +}; + +void nx_dpc_set_sync(u32 module_index, enum syncgenmode sync_gen_mode, + u32 avwidth, u32 avheight, u32 hsw, u32 hfp, + u32 hbp, u32 vsw, u32 vfp, u32 vbp, + enum polarity field_polarity, + enum polarity hsyncpolarity, + enum polarity vsyncpolarity, u32 even_vsw, + u32 even_vfp, u32 even_vbp, u32 vsetpixel, + u32 vsclrpixel, u32 evenvsetpixel, + u32 evenvsclrpixel); +void nx_dpc_set_output_format(u32 module_index, + enum outputformat output_format, + u8 output_video_config); +void nx_dpc_set_quantization_mode(u32 module_index, enum qmode rgb2yc, + enum qmode yc2rgb); +void nx_dpc_set_enable(u32 module_index, int enable, int rgbmode, + int use_ntscsync, int use_analog_output, + int seavenable); +void nx_dpc_set_enable_with_interlace(u32 module_index, int enable, + int rgbmode, int use_ntscsync, + int use_analog_output, + int seavenable); +void nx_dpc_set_enable_with_interlace(u32 module_index, int enable, + int rgbmode, int use_ntscsync, + int use_analog_output, + int seavenable); +void nx_dpc_set_out_video_clk_select(u32 module_index, + enum outpadclksel out_pad_vclk_sel); +void nx_dpc_set_reg_flush(u32 module_index); +void nx_dpc_set_sramon(u32 module_index); +void nx_dpc_set_sync_lcdtype(u32 module_index, int stnlcd, + int dual_view_enb, int bit_widh, + u8 cpcycle); +void nx_dpc_set_up_scale_control(u32 module_index, int up_scale_enb, + int filter_enb, u32 hscale, + u16 source_width); + +void nx_dpc_set_mputime(u32 module_index, u8 setup, u8 hold, u8 acc); +void nx_dpc_set_index(u32 module_index, u32 index); +void nx_dpc_set_data(u32 module_index, u32 data); +void nx_dpc_set_cmd_buffer_flush(u32 module_index); +void nx_dpc_set_cmd_buffer_clear(u32 module_index); +void nx_dpc_set_cmd_buffer_write(u32 module_index, u32 cmd_data); +void nx_dpc_set(u32 module_index); +u32 nx_dpc_get_data(u32 module_index); +u32 nx_dpc_get_status(u32 module_index); +void nx_dpc_rgbmask(u32 module_index, u32 rgbmask); +void nx_dpc_set_pad_location(u32 module_index, u32 index, u32 regvalue); +u32 nx_dpc_get_field_flag(u32 module_index); + +void nx_dpc_set_sync_v(u32 module_index, u32 avheight, u32 vsw, u32 vfp, + u32 vbp); + +int nx_dpc_init_reg_test(u32 module_index); +void nx_dpc_set_encoder_control_reg(u32 module_index, u32 param_a, + u32 param_b, u32 param_c); +void nx_dpc_set_encoder_shcphase_control(u32 module_index, + u32 chroma_param); +void nx_dpc_set_encoder_timing_config_reg(u32 module_index, u32 inctl); +void nx_dpc_set_encoder_dacoutput_select(u32 module_index, u8 dacsel0, + u8 dacsel1, u8 dacsel2, + u8 dacsel3, u8 dacsel4, + u8 dacsel5); +void nx_dpc_set_encoder_sync_location(u32 module_index, u16 hsoe, + u16 hsob, u16 vsob, u16 vsoe, + u8 vsost, int novrst); +void nx_dpc_set_encoder_dacpower_enable(u32 module_index, u8 dacpd); +void nx_dpc_set_ycorder(u32 module_index, enum nx_dpc_ycorder ycorder); +void nx_dpc_set_luma_gain(u32 module_index, u32 luma_gain); + +void nx_dpc_set_secondary_dpcsync(u32 module_index, int benb); +int nx_dpc_get_secondary_dpcsync(u32 module_index); +void nx_dpc_set_encenable(u32 module_index, int benb); +int nx_dpc_get_encenable(u32 module_index); +void nx_dpc_set_video_encoder_power_down(u32 module_index, int benb); +int nx_dpc_get_video_encoder_power_down(u32 module_index); +void nx_dpc_set_video_encoder_mode(u32 module_index, enum nx_dpc_vbs vbs, + int bpedestal); +void nx_dpc_set_video_encoder_schlock_control(u32 module_index, + int bfreerun); +int nx_dpc_get_video_encoder_schlock_control(u32 module_index); +void nx_dpc_set_video_encoder_bandwidth(u32 module_index, + enum nx_dpc_bandwidth luma, + enum nx_dpc_bandwidth chroma); +void nx_dpc_get_video_encoder_bandwidth(u32 module_index, + enum nx_dpc_bandwidth *pluma, + enum nx_dpc_bandwidth *pchroma); +void nx_dpc_set_video_encoder_color_control(u32 module_index, s8 sch, + s8 hue, s8 sat, + s8 crt, s8 brt); +void nx_dpc_get_video_encoder_color_control(u32 module_index, + s8 *psch, s8 *phue, + s8 *psat, s8 *pcrt, + s8 *pbrt); +void nx_dpc_set_video_encoder_fscadjust(u32 module_index, + int16_t adjust); +u16 nx_dpc_get_video_encoder_fscadjust(u32 module_index); +void nx_dpc_set_video_encoder_timing(u32 module_index, u32 hsos, + u32 hsoe, u32 vsos, u32 vsoe); +void nx_dpc_get_video_encoder_timing(u32 module_index, u32 *phsos, + u32 *phsoe, u32 *pvsos, + u32 *pvsoe); +void nx_dpc_set_sync_v(u32 module_index, u32 avheight, u32 vsw, u32 vfp, + u32 vbp); + +int nx_dpc_init_reg_test(u32 module_index); +void nx_dpc_set_encoder_control_reg(u32 module_index, u32 param_a, + u32 param_b, u32 param_c); +void nx_dpc_set_encoder_shcphase_control(u32 module_index, + u32 chroma_param); +void nx_dpc_set_encoder_timing_config_reg(u32 module_index, u32 inctl); +void nx_dpc_set_encoder_dacoutput_select(u32 module_index, u8 dacsel0, + u8 dacsel1, u8 dacsel2, + u8 dacsel3, u8 dacsel4, + u8 dacsel5); +void nx_dpc_set_encoder_sync_location(u32 module_index, u16 hsoe, + u16 hsob, u16 vsob, u16 vsoe, + u8 vsost, int novrst); +void nx_dpc_set_encoder_dacpower_enable(u32 module_index, u8 dacpd); +void nx_dpc_set_ycorder(u32 module_index, enum nx_dpc_ycorder ycorder); +void nx_dpc_set_luma_gain(u32 module_index, u32 luma_gain); + +#endif

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01: - nexell_display.c: Changed to DM, CONFIG_FB_ADDR can not be used anymore because framebuffer is allocated by video_reserve() in video-uclass.c. Therefore code changed appropriately.
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
drivers/video/Kconfig | 10 + drivers/video/Makefile | 1 + drivers/video/nexell/Kconfig | 27 ++ drivers/video/nexell/Makefile | 12 + drivers/video/nexell/s5pxx18_dp.c | 338 ++++++++++++++++ drivers/video/nexell/s5pxx18_dp_hdmi.c | 543 ++++++++++++++++++++++++++ drivers/video/nexell/s5pxx18_dp_lvds.c | 274 +++++++++++++ drivers/video/nexell/s5pxx18_dp_mipi.c | 677 +++++++++++++++++++++++++++++++++ drivers/video/nexell/s5pxx18_dp_rgb.c | 69 ++++ drivers/video/nexell_display.c | 658 ++++++++++++++++++++++++++++++++ 10 files changed, 2609 insertions(+) create mode 100644 drivers/video/nexell/Kconfig create mode 100644 drivers/video/nexell/Makefile create mode 100644 drivers/video/nexell/s5pxx18_dp.c create mode 100644 drivers/video/nexell/s5pxx18_dp_hdmi.c create mode 100644 drivers/video/nexell/s5pxx18_dp_lvds.c create mode 100644 drivers/video/nexell/s5pxx18_dp_mipi.c create mode 100644 drivers/video/nexell/s5pxx18_dp_rgb.c create mode 100644 drivers/video/nexell_display.c
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 50ab365..dd224ab 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -563,6 +563,16 @@ source "drivers/video/bridge/Kconfig"
source "drivers/video/imx/Kconfig"
+config VIDEO_NX + bool "Enable video support on Nexell SoC" + depends on ARCH_S5P6818 || ARCH_S5P4418 + help + Nexell SoC supports many video output options including eDP and + HDMI. This option enables this support which can be used on devices + which have an eDP display connected. + +source "drivers/video/nexell/Kconfig" + config VIDEO bool "Enable legacy video support" depends on !DM_VIDEO diff --git a/drivers/video/Makefile b/drivers/video/Makefile index df7119d..3bacc64 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -61,6 +61,7 @@ obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o obj-$(CONFIG_VIDEO_MVEBU) += mvebu_lcd.o obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o obj-$(CONFIG_VIDEO_MXS) += mxsfb.o videomodes.o +obj-$(CONFIG_VIDEO_NX) += nexell_display.o videomodes.o nexell/ obj-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o obj-$(CONFIG_VIDEO_DSI_HOST_SANDBOX) += sandbox_dsi_host.o obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o diff --git a/drivers/video/nexell/Kconfig b/drivers/video/nexell/Kconfig new file mode 100644 index 0000000..54b8ccb --- /dev/null +++ b/drivers/video/nexell/Kconfig @@ -0,0 +1,27 @@ +if VIDEO_NX + +menu "LCD select" + +config VIDEO_NX_RGB + bool "RGB LCD" + help + Support for RGB lcd output. + +config VIDEO_NX_LVDS + bool "LVDS LCD" + help + Support for LVDS lcd output. + +config VIDEO_NX_MIPI + bool "MiPi" + help + Support for MiPi lcd output. + +config VIDEO_NX_HDMI + bool "HDMI" + help + Support for hdmi output. + +endmenu + +endif diff --git a/drivers/video/nexell/Makefile b/drivers/video/nexell/Makefile new file mode 100644 index 0000000..111ab45 --- /dev/null +++ b/drivers/video/nexell/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2016 Nexell +# Junghyun, kimjhkim@nexell.co.kr + +obj-$(CONFIG_VIDEO_NX) += s5pxx18_dp.o +obj-$(CONFIG_VIDEO_NX) += soc/ + +obj-$(CONFIG_VIDEO_NX_RGB) += s5pxx18_dp_rgb.o +obj-$(CONFIG_VIDEO_NX_LVDS) += s5pxx18_dp_lvds.o +obj-$(CONFIG_VIDEO_NX_MIPI) += s5pxx18_dp_mipi.o +obj-$(CONFIG_VIDEO_NX_HDMI) += s5pxx18_dp_hdmi.o diff --git a/drivers/video/nexell/s5pxx18_dp.c b/drivers/video/nexell/s5pxx18_dp.c new file mode 100644 index 0000000..c69b69c --- /dev/null +++ b/drivers/video/nexell/s5pxx18_dp.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <config.h> +#include <common.h> +#include <errno.h> +#include <asm/arch/reset.h> +#include <asm/arch/nexell.h> +#include <asm/arch/display.h> + +#include "soc/s5pxx18_soc_disptop.h" +#include "soc/s5pxx18_soc_dpc.h" +#include "soc/s5pxx18_soc_mlc.h" + +#define MLC_LAYER_RGB_0 0 /* number of RGB layer 0 */ +#define MLC_LAYER_RGB_1 1 /* number of RGB layer 1 */ +#define MLC_LAYER_VIDEO 3 /* number of Video layer: 3 = VIDEO */ + +#define __io_address(a) (void *)(uintptr_t)(a) + +void dp_control_init(int module) +{ + void *base; + + /* top */ + base = __io_address(nx_disp_top_get_physical_address()); + nx_disp_top_set_base_address(base); + + /* control */ + base = __io_address(nx_dpc_get_physical_address(module)); + nx_dpc_set_base_address(module, base); + + /* top controller */ + nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_NEGATE); + + /* display controller */ + nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_NEGATE); + + nx_dpc_set_clock_pclk_mode(module, nx_pclkmode_always); +} + +int dp_control_setup(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl) +{ + unsigned int out_format; + unsigned int delay_mask; + int rgb_pvd = 0, hsync_cp1 = 7, vsync_fram = 7, de_cp2 = 7; + int v_vso = 1, v_veo = 1, e_vso = 1, e_veo = 1; + + int interlace = 0; + int invert_field; + int swap_rb; + unsigned int yc_order; + int vck_select; + int vclk_invert; + int emb_sync; + + enum nx_dpc_dither r_dither, g_dither, b_dither; + int rgb_mode = 0; + + if (NULL == sync || NULL == ctrl) { + debug("error, dp.%d not set sync or pad clock info !!!\n", + module); + return -EINVAL; + } + + out_format = ctrl->out_format; + delay_mask = ctrl->delay_mask; + interlace = sync->interlace; + invert_field = ctrl->invert_field; + swap_rb = ctrl->swap_RB; + yc_order = ctrl->yc_order; + vck_select = ctrl->vck_select; + vclk_invert = ctrl->clk_inv_lv0 | ctrl->clk_inv_lv1; + emb_sync = (out_format == DPC_FORMAT_CCIR656 ? 1 : 0); + + /* set delay mask */ + if (delay_mask & DP_SYNC_DELAY_RGB_PVD) + rgb_pvd = ctrl->d_rgb_pvd; + if (delay_mask & DP_SYNC_DELAY_HSYNC_CP1) + hsync_cp1 = ctrl->d_hsync_cp1; + if (delay_mask & DP_SYNC_DELAY_VSYNC_FRAM) + vsync_fram = ctrl->d_vsync_fram; + if (delay_mask & DP_SYNC_DELAY_DE_CP) + de_cp2 = ctrl->d_de_cp2; + + if (ctrl->vs_start_offset != 0 || + ctrl->vs_end_offset != 0 || + ctrl->ev_start_offset != 0 || ctrl->ev_end_offset != 0) { + v_vso = ctrl->vs_start_offset; + v_veo = ctrl->vs_end_offset; + e_vso = ctrl->ev_start_offset; + e_veo = ctrl->ev_end_offset; + } + + if (nx_dpc_format_rgb555 == out_format || + nx_dpc_format_mrgb555a == out_format || + nx_dpc_format_mrgb555b == out_format) { + r_dither = nx_dpc_dither_5bit; + g_dither = nx_dpc_dither_5bit; + b_dither = nx_dpc_dither_5bit; + rgb_mode = 1; + } else if (nx_dpc_format_rgb565 == out_format || + nx_dpc_format_mrgb565 == out_format) { + r_dither = nx_dpc_dither_5bit; + b_dither = nx_dpc_dither_5bit; + g_dither = nx_dpc_dither_6bit, rgb_mode = 1; + } else if ((nx_dpc_format_rgb666 == out_format) || + (nx_dpc_format_mrgb666 == out_format)) { + r_dither = nx_dpc_dither_6bit; + g_dither = nx_dpc_dither_6bit; + b_dither = nx_dpc_dither_6bit; + rgb_mode = 1; + } else { + r_dither = nx_dpc_dither_bypass; + g_dither = nx_dpc_dither_bypass; + b_dither = nx_dpc_dither_bypass; + rgb_mode = 1; + } + + /* CLKGEN0/1 */ + nx_dpc_set_clock_source(module, 0, ctrl->clk_src_lv0 == 3 ? + 6 : ctrl->clk_src_lv0); + nx_dpc_set_clock_divisor(module, 0, ctrl->clk_div_lv0); + nx_dpc_set_clock_source(module, 1, ctrl->clk_src_lv1); + nx_dpc_set_clock_divisor(module, 1, ctrl->clk_div_lv1); + nx_dpc_set_clock_out_delay(module, 0, ctrl->clk_delay_lv0); + nx_dpc_set_clock_out_delay(module, 1, ctrl->clk_delay_lv1); + + /* LCD out */ + nx_dpc_set_mode(module, out_format, interlace, invert_field, + rgb_mode, swap_rb, yc_order, emb_sync, emb_sync, + vck_select, vclk_invert, 0); + nx_dpc_set_hsync(module, sync->h_active_len, sync->h_sync_width, + sync->h_front_porch, sync->h_back_porch, + sync->h_sync_invert); + nx_dpc_set_vsync(module, sync->v_active_len, sync->v_sync_width, + sync->v_front_porch, sync->v_back_porch, + sync->v_sync_invert, sync->v_active_len, + sync->v_sync_width, sync->v_front_porch, + sync->v_back_porch); + nx_dpc_set_vsync_offset(module, v_vso, v_veo, e_vso, e_veo); + nx_dpc_set_delay(module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2); + nx_dpc_set_dither(module, r_dither, g_dither, b_dither); + +#ifdef CONFIG_MACH_S5P6818 + /* Set TFT_CLKCTRL (offset : 1030h) + * Field name : DPC0_CLKCTRL, DPC1_CLKCRL + * Default value : clk_inv_lv0/1 = 0 : PADCLK_InvCLK + * Invert case : clk_inv_lv0/1 = 1 : PADCLK_CLK + */ + if (module == 0 && ctrl->clk_inv_lv0) + nx_disp_top_set_padclock(padmux_primary_mlc, padclk_clk); + if (module == 1 && ctrl->clk_inv_lv1) + nx_disp_top_set_padclock(padmux_secondary_mlc, padclk_clk); +#endif + + debug("%s: dp.%d x:%4d, hf:%3d, hb:%3d, hs:%3d, hi=%d\n", + __func__, module, sync->h_active_len, sync->h_front_porch, + sync->h_back_porch, sync->h_sync_width, sync->h_sync_invert); + debug("%s: dp.%d y:%4d, vf:%3d, vb:%3d, vs:%3d, vi=%d\n", + __func__, module, sync->v_active_len, sync->v_front_porch, + sync->v_back_porch, sync->v_sync_width, sync->h_sync_invert); + debug("%s: dp.%d ck.0:%d:%d:%d, ck.1:%d:%d:%d\n", + __func__, module, + ctrl->clk_src_lv0, ctrl->clk_div_lv0, ctrl->clk_inv_lv0, + ctrl->clk_src_lv1, ctrl->clk_div_lv1, ctrl->clk_inv_lv1); + debug("%s: dp.%d vs:%d, ve:%d, es:%d, ee:%d\n", + __func__, module, v_vso, v_veo, e_vso, e_veo); + debug("%s: dp.%d delay RGB:%d, hs:%d, vs:%d, de:%d, fmt:0x%x\n", + __func__, module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2, + out_format); + + return 0; +} + +void dp_control_enable(int module, int on) +{ + debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF"); + + nx_dpc_set_dpc_enable(module, on); + nx_dpc_set_clock_divisor_enable(module, on); +} + +void dp_plane_init(int module) +{ + void *base = __io_address(nx_mlc_get_physical_address(module)); + + nx_mlc_set_base_address(module, base); + nx_mlc_set_clock_pclk_mode(module, nx_pclkmode_always); + nx_mlc_set_clock_bclk_mode(module, nx_bclkmode_always); +} + +int dp_plane_screen_setup(int module, struct dp_plane_top *top) +{ + int width = top->screen_width; + int height = top->screen_height; + int interlace = top->interlace; + int video_prior = top->video_prior; + unsigned int bg_color = top->back_color; + + /* MLC TOP layer */ + nx_mlc_set_screen_size(module, width, height); + nx_mlc_set_layer_priority(module, video_prior); + nx_mlc_set_background(module, bg_color); + nx_mlc_set_field_enable(module, interlace); + nx_mlc_set_rgblayer_gama_table_power_mode(module, 0, 0, 0); + nx_mlc_set_rgblayer_gama_table_sleep_mode(module, 1, 1, 1); + nx_mlc_set_rgblayer_gamma_enable(module, 0); + nx_mlc_set_dither_enable_when_using_gamma(module, 0); + nx_mlc_set_gamma_priority(module, 0); + nx_mlc_set_top_power_mode(module, 1); + nx_mlc_set_top_sleep_mode(module, 0); + + debug("%s: dp.%d screen %dx%d, %s, priority:%d, bg:0x%x\n", + __func__, module, width, height, + interlace ? "Interlace" : "Progressive", + video_prior, bg_color); + + return 0; +} + +void dp_plane_screen_enable(int module, int on) +{ + /* enable top screen */ + nx_mlc_set_mlc_enable(module, on); + nx_mlc_set_top_dirty_flag(module); + debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF"); +} + +int dp_plane_layer_setup(int module, struct dp_plane_info *plane) +{ + int sx = plane->left; + int sy = plane->top; + int ex = sx + plane->width - 1; + int ey = sy + plane->height - 1; + int pixel_byte = plane->pixel_byte; + int mem_lock_size = 16; /* fix mem lock size */ + int layer = plane->layer; + unsigned int format = plane->format; + + if (!plane->enable) + return -EINVAL; + + /* MLC layer */ + nx_mlc_set_lock_size(module, layer, mem_lock_size); + nx_mlc_set_alpha_blending(module, layer, 0, 15); + nx_mlc_set_transparency(module, layer, 0, 0); + nx_mlc_set_color_inversion(module, layer, 0, 0); + nx_mlc_set_rgblayer_invalid_position(module, layer, 0, 0, 0, 0, 0, 0); + nx_mlc_set_rgblayer_invalid_position(module, layer, 1, 0, 0, 0, 0, 0); + nx_mlc_set_format_rgb(module, layer, format); + nx_mlc_set_position(module, layer, sx, sy, ex, ey); + nx_mlc_set_rgblayer_stride(module, layer, pixel_byte, + plane->width * pixel_byte); + nx_mlc_set_rgblayer_address(module, layer, plane->fb_base); + + debug("%s: dp.%d.%d %d * %d, %dbpp, fmt:0x%x\n", + __func__, module, layer, plane->width, plane->height, + pixel_byte * 8, format); + debug("%s: b:0x%x, l:%d, t:%d, r:%d, b:%d, hs:%d, vs:%d\n", + __func__, plane->fb_base, sx, sy, ex, ey, + plane->width * pixel_byte, pixel_byte); + + return 0; +} + +int dp_plane_set_enable(int module, int layer, int on) +{ + int hl, hc; + int vl, vc; + + debug("%s: dp.%d.%d %s:%s\n", + __func__, module, layer, + layer == MLC_LAYER_VIDEO ? "Video" : "RGB", + on ? "ON" : "OFF"); + + if (layer != MLC_LAYER_VIDEO) { + nx_mlc_set_layer_enable(module, layer, on); + nx_mlc_set_dirty_flag(module, layer); + return 0; + } + + /* video layer */ + if (on) { + nx_mlc_set_video_layer_line_buffer_power_mode(module, 1); + nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 0); + nx_mlc_set_layer_enable(module, layer, 1); + nx_mlc_set_dirty_flag(module, layer); + } else { + nx_mlc_set_layer_enable(module, layer, 0); + nx_mlc_set_dirty_flag(module, layer); + nx_mlc_get_video_layer_scale_filter(module, + &hl, &hc, &vl, &vc); + if (hl || hc || vl || vc) + nx_mlc_set_video_layer_scale_filter(module, 0, 0, 0, 0); + nx_mlc_set_video_layer_line_buffer_power_mode(module, 0); + nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 1); + nx_mlc_set_dirty_flag(module, layer); + } + + return 0; +} + +void dp_plane_layer_enable(int module, + struct dp_plane_info *plane, int on) +{ + dp_plane_set_enable(module, plane->layer, on); +} + +int dp_plane_set_address(int module, int layer, unsigned int address) +{ + nx_mlc_set_rgblayer_address(module, layer, address); + nx_mlc_set_dirty_flag(module, layer); + + return 0; +} + +int dp_plane_wait_vsync(int module, int layer, int fps) +{ + int cnt = 0; + + if (fps == 0) + return (int)nx_mlc_get_dirty_flag(module, layer); + + while (fps > cnt++) { + while (nx_mlc_get_dirty_flag(module, layer)) + ; + nx_mlc_set_dirty_flag(module, layer); + } + return 0; +} diff --git a/drivers/video/nexell/s5pxx18_dp_hdmi.c b/drivers/video/nexell/s5pxx18_dp_hdmi.c new file mode 100644 index 0000000..5d72d1e --- /dev/null +++ b/drivers/video/nexell/s5pxx18_dp_hdmi.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <config.h> +#include <common.h> +#include <errno.h> + +#include <asm/arch/nexell.h> +#include <asm/arch/tieoff.h> +#include <asm/arch/reset.h> +#include <asm/arch/display.h> + +#include "soc/s5pxx18_soc_dpc.h" +#include "soc/s5pxx18_soc_hdmi.h" +#include "soc/s5pxx18_soc_disptop.h" +#include "soc/s5pxx18_soc_disptop_clk.h" + +#define __io_address(a) (void *)(uintptr_t)(a) + +static const u8 hdmiphy_preset74_25[32] = { + 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0xc8, 0x81, + 0xe8, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x0a, + 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x86, 0x54, + 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x10, 0x80, +}; + +static const u8 hdmiphy_preset148_5[32] = { + 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0xc8, 0x81, + 0xe8, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, 0x0a, + 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x86, 0x54, + 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80, +}; + +#define HDMIPHY_PRESET_TABLE_SIZE (32) + +enum NXP_HDMI_PRESET { + NXP_HDMI_PRESET_720P = 0, /* 1280 x 720 */ + NXP_HDMI_PRESET_1080P, /* 1920 x 1080 */ + NXP_HDMI_PRESET_MAX +}; + +static void hdmi_reset(void) +{ + nx_rstcon_setrst(RESET_ID_HDMI_VIDEO, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_HDMI_SPDIF, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_HDMI_TMDS, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_HDMI_VIDEO, RSTCON_NEGATE); + nx_rstcon_setrst(RESET_ID_HDMI_SPDIF, RSTCON_NEGATE); + nx_rstcon_setrst(RESET_ID_HDMI_TMDS, RSTCON_NEGATE); +} + +static int hdmi_phy_enable(int preset, int enable) +{ + const u8 *table = NULL; + int size = 0; + u32 addr, i = 0; + + if (!enable) + return 0; + + switch (preset) { + case NXP_HDMI_PRESET_720P: + table = hdmiphy_preset74_25; + size = 32; + break; + case NXP_HDMI_PRESET_1080P: + table = hdmiphy_preset148_5; + size = 31; + break; + default: + printf("hdmi: phy not support preset %d\n", preset); + return -EINVAL; + } + + nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (0 << 7)); + nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (0 << 7)); + nx_hdmi_set_reg(0, HDMI_PHY_REG04, (0 << 4)); + nx_hdmi_set_reg(0, HDMI_PHY_REG04, (0 << 4)); + nx_hdmi_set_reg(0, HDMI_PHY_REG24, (1 << 7)); + nx_hdmi_set_reg(0, HDMI_PHY_REG24, (1 << 7)); + + for (i = 0, addr = HDMI_PHY_REG04; size > i; i++, addr += 4) { + nx_hdmi_set_reg(0, addr, table[i]); + nx_hdmi_set_reg(0, addr, table[i]); + } + + nx_hdmi_set_reg(0, HDMI_PHY_REG7C, 0x80); + nx_hdmi_set_reg(0, HDMI_PHY_REG7C, 0x80); + nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (1 << 7)); + nx_hdmi_set_reg(0, HDMI_PHY_REG7C, (1 << 7)); + debug("%s: preset = %d\n", __func__, preset); + + return 0; +} + +static inline int hdmi_wait_phy_ready(void) +{ + int count = 500; + + do { + u32 val = nx_hdmi_get_reg(0, HDMI_LINK_PHY_STATUS_0); + + if (val & 0x01) { + printf("HDMI: phy ready...\n"); + return 1; + } + mdelay(10); + } while (count--); + + return 0; +} + +static inline int hdmi_get_vsync(int preset, + struct dp_sync_info *sync, + struct dp_ctrl_info *ctrl) +{ + switch (preset) { + case NXP_HDMI_PRESET_720P: /* 720p: 1280x720 */ + sync->h_active_len = 1280; + sync->h_sync_width = 40; + sync->h_back_porch = 220; + sync->h_front_porch = 110; + sync->h_sync_invert = 0; + sync->v_active_len = 720; + sync->v_sync_width = 5; + sync->v_back_porch = 20; + sync->v_front_porch = 5; + sync->v_sync_invert = 0; + break; + + case NXP_HDMI_PRESET_1080P: /* 1080p: 1920x1080 */ + sync->h_active_len = 1920; + sync->h_sync_width = 44; + sync->h_back_porch = 148; + sync->h_front_porch = 88; + sync->h_sync_invert = 0; + sync->v_active_len = 1080; + sync->v_sync_width = 5; + sync->v_back_porch = 36; + sync->v_front_porch = 4; + sync->v_sync_invert = 0; + break; + default: + printf("HDMI: not support preset sync %d\n", preset); + return -EINVAL; + } + + ctrl->clk_src_lv0 = 4; + ctrl->clk_div_lv0 = 1; + ctrl->clk_src_lv1 = 7; + ctrl->clk_div_lv1 = 1; + + ctrl->out_format = outputformat_rgb888; + ctrl->delay_mask = (DP_SYNC_DELAY_RGB_PVD | DP_SYNC_DELAY_HSYNC_CP1 | + DP_SYNC_DELAY_VSYNC_FRAM | DP_SYNC_DELAY_DE_CP); + ctrl->d_rgb_pvd = 0; + ctrl->d_hsync_cp1 = 0; + ctrl->d_vsync_fram = 0; + ctrl->d_de_cp2 = 7; + + /* HFP + HSW + HBP + AVWidth-VSCLRPIXEL- 1; */ + ctrl->vs_start_offset = (sync->h_front_porch + sync->h_sync_width + + sync->h_back_porch + sync->h_active_len - 1); + ctrl->vs_end_offset = 0; + + /* HFP + HSW + HBP + AVWidth-EVENVSCLRPIXEL- 1 */ + ctrl->ev_start_offset = (sync->h_front_porch + sync->h_sync_width + + sync->h_back_porch + sync->h_active_len - 1); + ctrl->ev_end_offset = 0; + debug("%s: preset: %d\n", __func__, preset); + + return 0; +} + +static void hdmi_clock(void) +{ + void *base = + __io_address(nx_disp_top_clkgen_get_physical_address + (to_mipi_clkgen)); + + nx_disp_top_clkgen_set_base_address(to_mipi_clkgen, base); + nx_disp_top_clkgen_set_clock_divisor_enable(to_mipi_clkgen, 0); + nx_disp_top_clkgen_set_clock_pclk_mode(to_mipi_clkgen, + nx_pclkmode_always); + nx_disp_top_clkgen_set_clock_source(to_mipi_clkgen, HDMI_SPDIF_CLKOUT, + 2); + nx_disp_top_clkgen_set_clock_divisor(to_mipi_clkgen, HDMI_SPDIF_CLKOUT, + 2); + nx_disp_top_clkgen_set_clock_source(to_mipi_clkgen, 1, 7); + nx_disp_top_clkgen_set_clock_divisor_enable(to_mipi_clkgen, 1); + + /* must initialize this !!! */ + nx_disp_top_hdmi_set_vsync_hsstart_end(0, 0); + nx_disp_top_hdmi_set_vsync_start(0); + nx_disp_top_hdmi_set_hactive_start(0); + nx_disp_top_hdmi_set_hactive_end(0); +} + +static void hdmi_vsync(struct dp_sync_info *sync) +{ + int width = sync->h_active_len; + int hsw = sync->h_sync_width; + int hbp = sync->h_back_porch; + int height = sync->v_active_len; + int vsw = sync->v_sync_width; + int vbp = sync->v_back_porch; + + int v_sync_s = vsw + vbp + height - 1; + int h_active_s = hsw + hbp; + int h_active_e = width + hsw + hbp; + int v_sync_hs_se0 = hsw + hbp + 1; + int v_sync_hs_se1 = hsw + hbp + 2; + + nx_disp_top_hdmi_set_vsync_start(v_sync_s); + nx_disp_top_hdmi_set_hactive_start(h_active_s); + nx_disp_top_hdmi_set_hactive_end(h_active_e); + nx_disp_top_hdmi_set_vsync_hsstart_end(v_sync_hs_se0, v_sync_hs_se1); +} + +static int hdmi_prepare(struct dp_sync_info *sync) +{ + int width = sync->h_active_len; + int hsw = sync->h_sync_width; + int hfp = sync->h_front_porch; + int hbp = sync->h_back_porch; + int height = sync->v_active_len; + int vsw = sync->v_sync_width; + int vfp = sync->v_front_porch; + int vbp = sync->v_back_porch; + + u32 h_blank, h_line, h_sync_start, h_sync_end; + u32 v_blank, v2_blank, v_line; + u32 v_sync_line_bef_1, v_sync_line_bef_2; + + u32 fixed_ffff = 0xffff; + + /* calculate sync variables */ + h_blank = hfp + hsw + hbp; + v_blank = vfp + vsw + vbp; + v2_blank = height + vfp + vsw + vbp; + v_line = height + vfp + vsw + vbp; /* total v */ + h_line = width + hfp + hsw + hbp; /* total h */ + h_sync_start = hfp; + h_sync_end = hfp + hsw; + v_sync_line_bef_1 = vfp; + v_sync_line_bef_2 = vfp + vsw; + + /* no blue screen mode, encoding order as it is */ + nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_0, (0 << 5) | (1 << 4)); + + /* set HDMI_LINK_BLUE_SCREEN_* to 0x0 */ + nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_R_0, 0x5555); + nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_R_1, 0x5555); + nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_G_0, 0x5555); + nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_G_1, 0x5555); + nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_B_0, 0x5555); + nx_hdmi_set_reg(0, HDMI_LINK_BLUE_SCREEN_B_1, 0x5555); + + /* set HDMI_CON_1 to 0x0 */ + nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_1, 0x0); + nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_2, 0x0); + + /* set interrupt : enable hpd_plug, hpd_unplug */ + nx_hdmi_set_reg(0, HDMI_LINK_INTC_CON_0, + (1 << 6) | (1 << 3) | (1 << 2)); + + /* set STATUS_EN to 0x17 */ + nx_hdmi_set_reg(0, HDMI_LINK_STATUS_EN, 0x17); + + /* TODO set HDP to 0x0 : later check hpd */ + nx_hdmi_set_reg(0, HDMI_LINK_HPD, 0x0); + + /* set MODE_SEL to 0x02 */ + nx_hdmi_set_reg(0, HDMI_LINK_MODE_SEL, 0x2); + + /* set H_BLANK_*, V1_BLANK_*, V2_BLANK_*, V_LINE_*, + * H_LINE_*, H_SYNC_START_*, H_SYNC_END_ * + * V_SYNC_LINE_BEF_1_*, V_SYNC_LINE_BEF_2_* + */ + nx_hdmi_set_reg(0, HDMI_LINK_H_BLANK_0, h_blank % 256); + nx_hdmi_set_reg(0, HDMI_LINK_H_BLANK_1, h_blank >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V1_BLANK_0, v_blank % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V1_BLANK_1, v_blank >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V2_BLANK_0, v2_blank % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V2_BLANK_1, v2_blank >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_LINE_0, v_line % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_LINE_1, v_line >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_H_LINE_0, h_line % 256); + nx_hdmi_set_reg(0, HDMI_LINK_H_LINE_1, h_line >> 8); + + if (width == 1280) { + nx_hdmi_set_reg(0, HDMI_LINK_HSYNC_POL, 0x1); + nx_hdmi_set_reg(0, HDMI_LINK_VSYNC_POL, 0x1); + } else { + nx_hdmi_set_reg(0, HDMI_LINK_HSYNC_POL, 0x0); + nx_hdmi_set_reg(0, HDMI_LINK_VSYNC_POL, 0x0); + } + + nx_hdmi_set_reg(0, HDMI_LINK_INT_PRO_MODE, 0x0); + + nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_START_0, (h_sync_start % 256) - 2); + nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_START_1, h_sync_start >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_END_0, (h_sync_end % 256) - 2); + nx_hdmi_set_reg(0, HDMI_LINK_H_SYNC_END_1, h_sync_end >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_1_0, + v_sync_line_bef_1 % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_1_1, + v_sync_line_bef_1 >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_2_0, + v_sync_line_bef_2 % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_BEF_2_1, + v_sync_line_bef_2 >> 8); + + /* Set V_SYNC_LINE_AFT*, V_SYNC_LINE_AFT_PXL*, VACT_SPACE* */ + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_1_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_1_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_2_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_2_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_3_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_3_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_4_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_4_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_5_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_5_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_6_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_6_1, fixed_ffff >> 8); + + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_1_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_2_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_3_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_4_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_5_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_V_SYNC_LINE_AFT_PXL_6_1, fixed_ffff >> 8); + + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE1_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE1_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE2_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE2_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE3_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE3_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE4_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE4_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE5_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE5_1, fixed_ffff >> 8); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE6_0, fixed_ffff % 256); + nx_hdmi_set_reg(0, HDMI_LINK_VACT_SPACE6_1, fixed_ffff >> 8); + + nx_hdmi_set_reg(0, HDMI_LINK_CSC_MUX, 0x0); + nx_hdmi_set_reg(0, HDMI_LINK_SYNC_GEN_MUX, 0x0); + + nx_hdmi_set_reg(0, HDMI_LINK_SEND_START_0, 0xfd); + nx_hdmi_set_reg(0, HDMI_LINK_SEND_START_1, 0x01); + nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_0, 0x0d); + nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_1, 0x3a); + nx_hdmi_set_reg(0, HDMI_LINK_SEND_END_2, 0x08); + + /* Set DC_CONTROL to 0x00 */ + nx_hdmi_set_reg(0, HDMI_LINK_DC_CONTROL, 0x0); + +#if CONFIG_HDMI_PATTERN + nx_hdmi_set_reg(0, HDMI_LINK_VIDEO_PATTERN_GEN, 0x1); +#else + nx_hdmi_set_reg(0, HDMI_LINK_VIDEO_PATTERN_GEN, 0x0); +#endif + + nx_hdmi_set_reg(0, HDMI_LINK_GCP_CON, 0x0a); + return 0; +} + +static void hdmi_init(void) +{ + void *base; + /** + * [SEQ 2] set the HDMI CLKGEN's PCLKMODE to always enabled + */ + base = + __io_address(nx_disp_top_clkgen_get_physical_address(hdmi_clkgen)); + nx_disp_top_clkgen_set_base_address(hdmi_clkgen, base); + nx_disp_top_clkgen_set_clock_pclk_mode(hdmi_clkgen, nx_pclkmode_always); + + base = __io_address(nx_hdmi_get_physical_address(0)); + nx_hdmi_set_base_address(0, base); + + /** + * [SEQ 3] set the 0xC001100C[0] to 1 + */ + nx_tieoff_set(NX_TIEOFF_DISPLAYTOP0_i_HDMI_PHY_REFCLK_SEL, 1); + + /** + * [SEQ 4] release the resets of HDMI.i_PHY_nRST and HDMI.i_nRST + */ + nx_rstcon_setrst(RESET_ID_HDMI_PHY, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_HDMI, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_HDMI_PHY, RSTCON_NEGATE); + nx_rstcon_setrst(RESET_ID_HDMI, RSTCON_NEGATE); +} + +void hdmi_enable(int input, int preset, struct dp_sync_info *sync, int enable) +{ + if (enable) { + nx_hdmi_set_reg(0, HDMI_LINK_HDMI_CON_0, + (nx_hdmi_get_reg(0, HDMI_LINK_HDMI_CON_0) | + 0x1)); + hdmi_vsync(sync); + } else { + hdmi_phy_enable(preset, 0); + } +} + +static int hdmi_setup(int input, int preset, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl) +{ + u32 HDMI_SEL = 0; + int ret; + + switch (input) { + case DP_DEVICE_DP0: + HDMI_SEL = primary_mlc; + break; + case DP_DEVICE_DP1: + HDMI_SEL = secondary_mlc; + break; + case DP_DEVICE_RESCONV: + HDMI_SEL = resolution_conv; + break; + default: + printf("HDMI: not support source device %d\n", input); + return -EINVAL; + } + + /** + * [SEQ 5] set up the HDMI PHY to specific video clock. + */ + ret = hdmi_phy_enable(preset, 1); + if (ret < 0) + return ret; + + /** + * [SEQ 6] I2S (or SPDIFTX) configuration for the source audio data + * this is done in another user app - ex> Android Audio HAL + */ + + /** + * [SEQ 7] Wait for ECID ready + */ + + /** + * [SEQ 8] release the resets of HDMI.i_VIDEO_nRST and HDMI.i_SPDIF_nRST + * and HDMI.i_TMDS_nRST + */ + hdmi_reset(); + + /** + * [SEQ 9] Wait for HDMI PHY ready (wait until 0xC0200020.[0], 1) + */ + if (hdmi_wait_phy_ready() == 0) { + printf("%s: failed to wait for hdmiphy ready\n", __func__); + hdmi_phy_enable(preset, 0); + return -EIO; + } + /* set mux */ + nx_disp_top_set_hdmimux(1, HDMI_SEL); + + /** + * [SEC 10] Set the DPC CLKGEN's Source Clock to HDMI_CLK & + * Set Sync Parameter + */ + hdmi_clock(); + /* set hdmi link clk to clkgen vs default is hdmi phy clk */ + + /** + * [SEQ 11] Set up the HDMI Converter parameters + */ + hdmi_get_vsync(preset, sync, ctrl); + hdmi_prepare(sync); + + return 0; +} + +void nx_hdmi_display(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_plane_top *top, struct dp_plane_info *planes, + struct dp_hdmi_dev *dev) +{ + struct dp_plane_info *plane = planes; + int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1; + int count = top->plane_num; + int preset = dev->preset; + int i = 0; + + debug("HDMI: display.%d\n", module); + + switch (preset) { + case 0: + top->screen_width = 1280; + top->screen_height = 720; + sync->h_active_len = 1280; + sync->v_active_len = 720; + break; + case 1: + top->screen_width = 1920; + top->screen_height = 1080; + sync->h_active_len = 1920; + sync->v_active_len = 1080; + break; + default: + printf("hdmi not support preset %d\n", preset); + return; + } + + printf("HDMI: display.%d, preset %d (%4d * %4d)\n", + module, preset, top->screen_width, top->screen_height); + + dp_control_init(module); + dp_plane_init(module); + + hdmi_init(); + hdmi_setup(input, preset, sync, ctrl); + + dp_plane_screen_setup(module, top); + for (i = 0; count > i; i++, plane++) { + if (!plane->enable) + continue; + dp_plane_layer_setup(module, plane); + dp_plane_layer_enable(module, plane, 1); + } + dp_plane_screen_enable(module, 1); + + dp_control_setup(module, sync, ctrl); + dp_control_enable(module, 1); + + hdmi_enable(input, preset, sync, 1); +} diff --git a/drivers/video/nexell/s5pxx18_dp_lvds.c b/drivers/video/nexell/s5pxx18_dp_lvds.c new file mode 100644 index 0000000..f8ea63f --- /dev/null +++ b/drivers/video/nexell/s5pxx18_dp_lvds.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <config.h> +#include <common.h> +#include <errno.h> + +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/display.h> + +#include "soc/s5pxx18_soc_lvds.h" +#include "soc/s5pxx18_soc_disptop.h" +#include "soc/s5pxx18_soc_disptop_clk.h" + +#define __io_address(a) (void *)(uintptr_t)(a) + +static void lvds_phy_reset(void) +{ + nx_rstcon_setrst(RESET_ID_LVDS, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_LVDS, RSTCON_NEGATE); +} + +static void lvds_init(void) +{ + int clkid = DP_CLOCK_LVDS; + int index = 0; + void *base; + + base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid)); + nx_disp_top_clkgen_set_base_address(clkid, base); + + nx_lvds_initialize(); + + for (index = 0; nx_lvds_get_number_of_module() > index; index++) + nx_lvds_set_base_address(index, + (void *)__io_address(nx_lvds_get_physical_address(index))); + + nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always); +} + +static void lvds_enable(int enable) +{ + int clkid = DP_CLOCK_LVDS; + int on = (enable ? 1 : 0); + + nx_disp_top_clkgen_set_clock_divisor_enable(clkid, on); +} + +static int lvds_setup(int module, int input, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_lvds_dev *dev) +{ + unsigned int val; + int clkid = DP_CLOCK_LVDS; + enum dp_lvds_format format = DP_LVDS_FORMAT_JEIDA; + u32 voltage = DEF_VOLTAGE_LEVEL; + + if (dev) { + format = dev->lvds_format; + voltage = dev->voltage_level; + } + + printf("LVDS: "); + printf("%s, ", format == DP_LVDS_FORMAT_VESA ? "VESA" : + format == DP_LVDS_FORMAT_JEIDA ? "JEIDA" : "LOC"); + printf("voltage LV:0x%x\n", voltage); + + /* + *-------- predefined type. + * only change iTA to iTE in VESA mode + * wire [34:0] loc_VideoIn = + * {4'hf, 4'h0, i_VDEN, i_VSYNC, i_HSYNC, i_VD[23:0] }; + */ + u32 VSYNC = 25; + u32 HSYNC = 24; + u32 VDEN = 26; /* bit position */ + u32 ONE = 34; + u32 ZERO = 27; + + /*==================================================== + * current not use location mode + *==================================================== + */ + u32 LOC_A[7] = {ONE, ONE, ONE, ONE, ONE, ONE, ONE}; + u32 LOC_B[7] = {ONE, ONE, ONE, ONE, ONE, ONE, ONE}; + u32 LOC_C[7] = {VDEN, VSYNC, HSYNC, ONE, HSYNC, VSYNC, VDEN}; + u32 LOC_D[7] = {ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO}; + u32 LOC_E[7] = {ZERO, ZERO, ZERO, ZERO, ZERO, ZERO, ZERO}; + + switch (input) { + case DP_DEVICE_DP0: + input = 0; + break; + case DP_DEVICE_DP1: + input = 1; + break; + case DP_DEVICE_RESCONV: + input = 2; + break; + default: + return -EINVAL; + } + + /* + * select TOP MUX + */ + nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 0); + nx_disp_top_clkgen_set_clock_source(clkid, 0, ctrl->clk_src_lv0); + nx_disp_top_clkgen_set_clock_divisor(clkid, 0, ctrl->clk_div_lv0); + nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv1); + nx_disp_top_clkgen_set_clock_divisor(clkid, 1, ctrl->clk_div_lv1); + + /* + * LVDS Control Pin Setting + */ + val = (0 << 30) | /* CPU_I_VBLK_FLAG_SEL */ + (0 << 29) | /* CPU_I_BVLK_FLAG */ + (1 << 28) | /* SKINI_BST */ + (1 << 27) | /* DLYS_BST */ + (0 << 26) | /* I_AUTO_SEL */ + (format << 19) | /* JEiDA data packing */ + (0x1B << 13) | /* I_LOCK_PPM_SET, PPM setting for PLL lock */ + (0x638 << 1); /* I_DESKEW_CNT_SEL, period of de-skew region */ + nx_lvds_set_lvdsctrl0(0, val); + + val = (0 << 28) | /* I_ATE_MODE, function mode */ + (0 << 27) | /* I_TEST_CON_MODE, DA (test ctrl mode) */ + (0 << 24) | /* I_TX4010X_DUMMY */ + (0 << 15) | /* SKCCK 0 */ + (0 << 12) | /* SKC4 (TX output skew control pin at ODD ch4) */ + (0 << 9) | /* SKC3 (TX output skew control pin at ODD ch3) */ + (0 << 6) | /* SKC2 (TX output skew control pin at ODD ch2) */ + (0 << 3) | /* SKC1 (TX output skew control pin at ODD ch1) */ + (0 << 0); /* SKC0 (TX output skew control pin at ODD ch0) */ + nx_lvds_set_lvdsctrl1(0, val); + + val = (0 << 15) | /* CK_POL_SEL, Input clock, bypass */ + (0 << 14) | /* VSEL, VCO Freq. range. 0: Low(40MHz~90MHz), + * 1: High(90MHz~160MHz) */ + (0x1 << 12) | /* S (Post-scaler) */ + (0xA << 6) | /* M (Main divider) */ + (0xA << 0); /* P (Pre-divider) */ + + nx_lvds_set_lvdsctrl2(0, val); + val = (0x03 << 6) | /* SK_BIAS, Bias current ctrl pin */ + (0 << 5) | /* SKEWINI, skew selection pin, 0: bypass, + * 1: skew enable */ + (0 << 4) | /* SKEW_EN_H, skew block power down, 0: power down, + * 1: operating */ + (1 << 3) | /* CNTB_TDLY, delay control pin */ + (0 << 2) | /* SEL_DATABF, input clock 1/2 division cont. pin */ + (0x3 << 0); /* SKEW_REG_CUR, regulator bias current selection + * in SKEW block */ + + nx_lvds_set_lvdsctrl3(0, val); + val = (0 << 28) | /* FLT_CNT, filter control pin for PLL */ + (0 << 27) | /* VOD_ONLY_CNT, the pre-emphasis's pre-diriver + * control pin (VOD only) */ + (0 << 26) | /* CNNCT_MODE_SEL, connectivity mode selection, + * 0:TX operating, 1:con check */ + (0 << 24) | /* CNNCT_CNT, connectivity ctrl pin, + * 0: tx operating, 1: con check */ + (0 << 23) | /* VOD_HIGH_S, VOD control pin, 1: Vod only */ + (0 << 22) | /* SRC_TRH, source termination resistor sel. pin */ + (voltage << 14) | + (0x01 << 6) | /* CNT_PEN_H, TX driver pre-emphasis level cont. */ + (0x4 << 3) | /* FC_CODE, vos control pin */ + (0 << 2) | /* OUTCON, TX Driver state selectioin pin, 0:Hi-z, + * 1:Low */ + (0 << 1) | /* LOCK_CNT, Lock signal selection pin, enable */ + (0 << 0); /* AUTO_DSK_SEL, auto deskew sel. pin, normal */ + nx_lvds_set_lvdsctrl4(0, val); + + val = (0 << 24) | /* I_BIST_RESETB */ + (0 << 23) | /* I_BIST_EN */ + (0 << 21) | /* I_BIST_PAT_SEL */ + (0 << 14) | /* I_BIST_USER_PATTERN */ + (0 << 13) | /* I_BIST_FORCE_ERROR */ + (0 << 7) | /* I_BIST_SKEW_CTRL */ + (0 << 5) | /* I_BIST_CLK_INV */ + (0 << 3) | /* I_BIST_DATA_INV */ + (0 << 0); /* I_BIST_CH_SEL */ + nx_lvds_set_lvdstmode0(0, val); + + /* user do not need to modify this codes. */ + val = (LOC_A[4] << 24) | (LOC_A[3] << 18) | (LOC_A[2] << 12) | + (LOC_A[1] << 6) | (LOC_A[0] << 0); + nx_lvds_set_lvdsloc0(0, val); + + val = (LOC_B[2] << 24) | (LOC_B[1] << 18) | (LOC_B[0] << 12) | + (LOC_A[6] << 6) | (LOC_A[5] << 0); + nx_lvds_set_lvdsloc1(0, val); + + val = (LOC_C[0] << 24) | (LOC_B[6] << 18) | (LOC_B[5] << 12) | + (LOC_B[4] << 6) | (LOC_B[3] << 0); + nx_lvds_set_lvdsloc2(0, val); + + val = (LOC_C[5] << 24) | (LOC_C[4] << 18) | (LOC_C[3] << 12) | + (LOC_C[2] << 6) | (LOC_C[1] << 0); + nx_lvds_set_lvdsloc3(0, val); + + val = (LOC_D[3] << 24) | (LOC_D[2] << 18) | (LOC_D[1] << 12) | + (LOC_D[0] << 6) | (LOC_C[6] << 0); + nx_lvds_set_lvdsloc4(0, val); + + val = (LOC_E[1] << 24) | (LOC_E[0] << 18) | (LOC_D[6] << 12) | + (LOC_D[5] << 6) | (LOC_D[4] << 0); + nx_lvds_set_lvdsloc5(0, val); + + val = (LOC_E[6] << 24) | (LOC_E[5] << 18) | (LOC_E[4] << 12) | + (LOC_E[3] << 6) | (LOC_E[2] << 0); + nx_lvds_set_lvdsloc6(0, val); + + nx_lvds_set_lvdslocmask0(0, 0xffffffff); + nx_lvds_set_lvdslocmask1(0, 0xffffffff); + + nx_lvds_set_lvdslocpol0(0, (0 << 19) | (0 << 18)); + + /* + * select TOP MUX + */ + nx_disp_top_set_lvdsmux(1, input); + + /* + * LVDS PHY Reset, make sure last. + */ + lvds_phy_reset(); + + return 0; +} + +void nx_lvds_display(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_plane_top *top, struct dp_plane_info *planes, + struct dp_lvds_dev *dev) +{ + struct dp_plane_info *plane = planes; + int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1; + int count = top->plane_num; + int i = 0; + + printf("LVDS: dp.%d\n", module); + + dp_control_init(module); + dp_plane_init(module); + + lvds_init(); + + /* set plane */ + dp_plane_screen_setup(module, top); + + for (i = 0; count > i; i++, plane++) { + if (!plane->enable) + continue; + dp_plane_layer_setup(module, plane); + dp_plane_layer_enable(module, plane, 1); + } + + dp_plane_screen_enable(module, 1); + + /* set lvds */ + lvds_setup(module, input, sync, ctrl, dev); + + lvds_enable(1); + + /* set dp control */ + dp_control_setup(module, sync, ctrl); + dp_control_enable(module, 1); +} diff --git a/drivers/video/nexell/s5pxx18_dp_mipi.c b/drivers/video/nexell/s5pxx18_dp_mipi.c new file mode 100644 index 0000000..670272b --- /dev/null +++ b/drivers/video/nexell/s5pxx18_dp_mipi.c @@ -0,0 +1,677 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <config.h> +#include <common.h> +#include <errno.h> + +#include <asm/arch/nexell.h> +#include <asm/arch/tieoff.h> +#include <asm/arch/reset.h> +#include <asm/arch/display.h> + +#include "soc/s5pxx18_soc_mipi.h" +#include "soc/s5pxx18_soc_disptop.h" +#include "soc/s5pxx18_soc_disptop_clk.h" + +#define PLLPMS_1000MHZ 0x33E8 +#define BANDCTL_1000MHZ 0xF +#define PLLPMS_960MHZ 0x2280 +#define BANDCTL_960MHZ 0xF +#define PLLPMS_900MHZ 0x2258 +#define BANDCTL_900MHZ 0xE +#define PLLPMS_840MHZ 0x2230 +#define BANDCTL_840MHZ 0xD +#define PLLPMS_750MHZ 0x43E8 +#define BANDCTL_750MHZ 0xC +#define PLLPMS_660MHZ 0x21B8 +#define BANDCTL_660MHZ 0xB +#define PLLPMS_600MHZ 0x2190 +#define BANDCTL_600MHZ 0xA +#define PLLPMS_540MHZ 0x2168 +#define BANDCTL_540MHZ 0x9 +#define PLLPMS_512MHZ 0x03200 +#define BANDCTL_512MHZ 0x9 +#define PLLPMS_480MHZ 0x2281 +#define BANDCTL_480MHZ 0x8 +#define PLLPMS_420MHZ 0x2231 +#define BANDCTL_420MHZ 0x7 +#define PLLPMS_402MHZ 0x2219 +#define BANDCTL_402MHZ 0x7 +#define PLLPMS_330MHZ 0x21B9 +#define BANDCTL_330MHZ 0x6 +#define PLLPMS_300MHZ 0x2191 +#define BANDCTL_300MHZ 0x5 +#define PLLPMS_210MHZ 0x2232 +#define BANDCTL_210MHZ 0x4 +#define PLLPMS_180MHZ 0x21E2 +#define BANDCTL_180MHZ 0x3 +#define PLLPMS_150MHZ 0x2192 +#define BANDCTL_150MHZ 0x2 +#define PLLPMS_100MHZ 0x3323 +#define BANDCTL_100MHZ 0x1 +#define PLLPMS_80MHZ 0x3283 +#define BANDCTL_80MHZ 0x0 + +#define MIPI_INDEX 0 +#define MIPI_EXC_PRE_VALUE 1 +#define MIPI_DSI_IRQ_MASK 29 + +#define __io_address(a) (void *)(uintptr_t)(a) + +struct mipi_xfer_msg { + u8 id, data[2]; + u16 flags; + const u8 *tx_buf; + u16 tx_len; + u8 *rx_buf; + u16 rx_len; +}; + +static void mipi_reset(void) +{ + /* tieoff */ + nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAA, 3); + nx_tieoff_set(NX_TIEOFF_MIPI0_NX_DPSRAM_1R1W_EMAB, 3); + + /* reset */ + nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_MIPI_CSI, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_ASSERT); + nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_ASSERT); + + nx_rstcon_setrst(RESET_ID_MIPI, RSTCON_NEGATE); + nx_rstcon_setrst(RESET_ID_MIPI_DSI, RSTCON_NEGATE); + nx_rstcon_setrst(RESET_ID_MIPI_PHY_S, RSTCON_NEGATE); + nx_rstcon_setrst(RESET_ID_MIPI_PHY_M, RSTCON_NEGATE); +} + +static void mipi_init(void) +{ + int clkid = DP_CLOCK_MIPI; + void *base; + + /* + * neet to reset before open + */ + mipi_reset(); + + base = __io_address(nx_disp_top_clkgen_get_physical_address(clkid)); + nx_disp_top_clkgen_set_base_address(clkid, base); + nx_disp_top_clkgen_set_clock_pclk_mode(clkid, nx_pclkmode_always); + + base = __io_address(nx_mipi_get_physical_address(0)); + nx_mipi_set_base_address(0, base); +} + +static int mipi_get_phy_pll(int bitrate, unsigned int *pllpms, + unsigned int *bandctl) +{ + unsigned int pms, ctl; + + switch (bitrate) { + case 1000: + pms = PLLPMS_1000MHZ; + ctl = BANDCTL_1000MHZ; + break; + case 960: + pms = PLLPMS_960MHZ; + ctl = BANDCTL_960MHZ; + break; + case 900: + pms = PLLPMS_900MHZ; + ctl = BANDCTL_900MHZ; + break; + case 840: + pms = PLLPMS_840MHZ; + ctl = BANDCTL_840MHZ; + break; + case 750: + pms = PLLPMS_750MHZ; + ctl = BANDCTL_750MHZ; + break; + case 660: + pms = PLLPMS_660MHZ; + ctl = BANDCTL_660MHZ; + break; + case 600: + pms = PLLPMS_600MHZ; + ctl = BANDCTL_600MHZ; + break; + case 540: + pms = PLLPMS_540MHZ; + ctl = BANDCTL_540MHZ; + break; + case 512: + pms = PLLPMS_512MHZ; + ctl = BANDCTL_512MHZ; + break; + case 480: + pms = PLLPMS_480MHZ; + ctl = BANDCTL_480MHZ; + break; + case 420: + pms = PLLPMS_420MHZ; + ctl = BANDCTL_420MHZ; + break; + case 402: + pms = PLLPMS_402MHZ; + ctl = BANDCTL_402MHZ; + break; + case 330: + pms = PLLPMS_330MHZ; + ctl = BANDCTL_330MHZ; + break; + case 300: + pms = PLLPMS_300MHZ; + ctl = BANDCTL_300MHZ; + break; + case 210: + pms = PLLPMS_210MHZ; + ctl = BANDCTL_210MHZ; + break; + case 180: + pms = PLLPMS_180MHZ; + ctl = BANDCTL_180MHZ; + break; + case 150: + pms = PLLPMS_150MHZ; + ctl = BANDCTL_150MHZ; + break; + case 100: + pms = PLLPMS_100MHZ; + ctl = BANDCTL_100MHZ; + break; + case 80: + pms = PLLPMS_80MHZ; + ctl = BANDCTL_80MHZ; + break; + default: + return -EINVAL; + } + + *pllpms = pms; + *bandctl = ctl; + + return 0; +} + +static int mipi_prepare(int module, int input, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_mipi_dev *mipi) +{ + int index = MIPI_INDEX; + u32 esc_pre_value = MIPI_EXC_PRE_VALUE; + int lpm = mipi->lpm_trans; + int ret = 0; + + ret = mipi_get_phy_pll(mipi->hs_bitrate, + &mipi->hs_pllpms, &mipi->hs_bandctl); + if (ret < 0) + return ret; + + ret = mipi_get_phy_pll(mipi->lp_bitrate, + &mipi->lp_pllpms, &mipi->lp_bandctl); + if (ret < 0) + return ret; + + debug("%s: mipi lp:%dmhz:0x%x:0x%x, hs:%dmhz:0x%x:0x%x, %s trans\n", + __func__, mipi->lp_bitrate, mipi->lp_pllpms, mipi->lp_bandctl, + mipi->hs_bitrate, mipi->hs_pllpms, mipi->hs_bandctl, + lpm ? "low" : "high"); + + if (lpm) + nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF, + mipi->lp_pllpms, mipi->lp_bandctl, 0, 0); + else + nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF, + mipi->hs_pllpms, mipi->hs_bandctl, 0, 0); + +#ifdef CONFIG_ARCH_S5P4418 + /* + * disable the escape clock generating prescaler + * before soft reset. + */ + nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 0, 10); + mdelay(1); +#endif + + nx_mipi_dsi_software_reset(index); + nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, 1, esc_pre_value); + nx_mipi_dsi_set_phy(index, 0, 1, 1, 0, 0, 0, 0, 0); + + if (lpm) + nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_lp, + nx_mipi_dsi_lpmode_lp); + else + nx_mipi_dsi_set_escape_lp(index, nx_mipi_dsi_lpmode_hs, + nx_mipi_dsi_lpmode_hs); + mdelay(20); + + return 0; +} + +static int mipi_enable(int module, int input, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_mipi_dev *mipi) +{ + struct mipi_dsi_device *dsi = &mipi->dsi; + int clkid = DP_CLOCK_MIPI; + int index = MIPI_INDEX; + int width = sync->h_active_len; + int height = sync->v_active_len; + int HFP = sync->h_front_porch; + int HBP = sync->h_back_porch; + int HS = sync->h_sync_width; + int VFP = sync->v_front_porch; + int VBP = sync->v_back_porch; + int VS = sync->v_sync_width; + int en_prescaler = 1; + u32 esc_pre_value = MIPI_EXC_PRE_VALUE; + + int txhsclock = 1; + int lpm = mipi->lpm_trans; + bool command_mode = mipi->command_mode; + + enum nx_mipi_dsi_format dsi_format; + int data_len = dsi->lanes - 1; + bool burst = dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST ? true : false; + bool eot_enable = dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET ? + false : true; + + /* + * disable the escape clock generating prescaler + * before soft reset. + */ +#ifdef CONFIG_ARCH_S5P4418 + en_prescaler = 0; +#endif + + debug("%s: mode:%s, lanes.%d\n", __func__, + command_mode ? "command" : "video", data_len + 1); + + if (lpm) + nx_mipi_dsi_set_escape_lp(index, + nx_mipi_dsi_lpmode_hs, + nx_mipi_dsi_lpmode_hs); + + nx_mipi_dsi_set_pll(index, 1, 0xFFFFFFFF, + mipi->hs_pllpms, mipi->hs_bandctl, 0, 0); + mdelay(1); + + nx_mipi_dsi_set_clock(index, 0, 0, 1, 1, 1, 0, 0, 0, en_prescaler, 10); + mdelay(1); + + nx_mipi_dsi_software_reset(index); + nx_mipi_dsi_set_clock(index, txhsclock, 0, 1, + 1, 1, 0, 0, 0, 1, esc_pre_value); + + switch (data_len) { + case 0: /* 1 lane */ + nx_mipi_dsi_set_phy(index, data_len, 1, 1, 0, 0, 0, 0, 0); + break; + case 1: /* 2 lane */ + nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 0, 0, 0, 0); + break; + case 2: /* 3 lane */ + nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 0, 0, 0); + break; + case 3: /* 3 lane */ + nx_mipi_dsi_set_phy(index, data_len, 1, 1, 1, 1, 1, 0, 0); + break; + default: + printf("%s: not support data lanes %d\n", + __func__, data_len + 1); + return -EINVAL; + } + + switch (dsi->format) { + case MIPI_DSI_FMT_RGB565: + dsi_format = nx_mipi_dsi_format_rgb565; + break; + case MIPI_DSI_FMT_RGB666: + dsi_format = nx_mipi_dsi_format_rgb666; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + dsi_format = nx_mipi_dsi_format_rgb666_packed; + break; + case MIPI_DSI_FMT_RGB888: + dsi_format = nx_mipi_dsi_format_rgb888; + break; + default: + printf("%s: not support format %d\n", __func__, dsi->format); + return -EINVAL; + } + + nx_mipi_dsi_set_config_video_mode(index, 1, 0, burst, + nx_mipi_dsi_syncmode_event, + eot_enable, 1, 1, 1, 1, 0, dsi_format, + HFP, HBP, HS, VFP, VBP, VS, 0); + + nx_mipi_dsi_set_size(index, width, height); + + /* set mux */ + nx_disp_top_set_mipimux(1, module); + + /* 0 is spdif, 1 is mipi vclk */ + nx_disp_top_clkgen_set_clock_source(clkid, 1, ctrl->clk_src_lv0); + nx_disp_top_clkgen_set_clock_divisor(clkid, 1, + ctrl->clk_div_lv1 * + ctrl->clk_div_lv0); + + /* SPDIF and MIPI */ + nx_disp_top_clkgen_set_clock_divisor_enable(clkid, 1); + + /* START: CLKGEN, MIPI is started in setup function */ + nx_disp_top_clkgen_set_clock_divisor_enable(clkid, true); + nx_mipi_dsi_set_enable(index, true); + + return 0; +} + +static int nx_mipi_transfer_tx(struct mipi_dsi_device *dsi, + struct mipi_xfer_msg *xfer) +{ + const u8 *txb; + int size, index = 0; + u32 data; + + if (xfer->tx_len > DSI_TX_FIFO_SIZE) + printf("warn: tx %d size over fifo %d\n", + (int)xfer->tx_len, DSI_TX_FIFO_SIZE); + + /* write payload */ + size = xfer->tx_len; + txb = xfer->tx_buf; + + while (size >= 4) { + data = (txb[3] << 24) | (txb[2] << 16) | + (txb[1] << 8) | (txb[0]); + nx_mipi_dsi_write_payload(index, data); + txb += 4, size -= 4; + data = 0; + } + + switch (size) { + case 3: + data |= txb[2] << 16; + case 2: + data |= txb[1] << 8; + case 1: + data |= txb[0]; + nx_mipi_dsi_write_payload(index, data); + break; + case 0: + break; /* no payload */ + } + + /* write packet hdr */ + data = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->id; + + nx_mipi_dsi_write_pkheader(index, data); + + return 0; +} + +static int nx_mipi_transfer_done(struct mipi_dsi_device *dsi) +{ + int index = 0, count = 100; + u32 value; + + do { + mdelay(1); + value = nx_mipi_dsi_read_fifo_status(index); + if (((1 << 22) & value)) + break; + } while (count-- > 0); + + if (count < 0) + return -EINVAL; + + return 0; +} + +static int nx_mipi_transfer_rx(struct mipi_dsi_device *dsi, + struct mipi_xfer_msg *xfer) +{ + u8 *rxb = xfer->rx_buf; + int index = 0, rx_len = 0; + u32 data, count = 0; + u16 size; + int err = -EINVAL; + + nx_mipi_dsi_clear_interrupt_pending(index, 18); + + while (1) { + /* Completes receiving data. */ + if (nx_mipi_dsi_get_interrupt_pending(index, 18)) + break; + + mdelay(1); + + if (count > 500) { + printf("%s: error recevice data\n", __func__); + err = -EINVAL; + goto clear_fifo; + } else { + count++; + } + } + + data = nx_mipi_dsi_read_fifo(index); + + switch (data & 0x3f) { + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: + if (xfer->rx_len >= 2) { + rxb[1] = data >> 16; + rx_len++; + } + + /* Fall through */ + case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: + case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: + rxb[0] = data >> 8; + rx_len++; + xfer->rx_len = rx_len; + err = rx_len; + goto clear_fifo; + + case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: + printf("DSI Error Report: 0x%04x\n", (data >> 8) & 0xffff); + err = rx_len; + goto clear_fifo; + } + + size = (data >> 8) & 0xffff; + + if (size > xfer->rx_len) + size = xfer->rx_len; + else if (size < xfer->rx_len) + xfer->rx_len = size; + + size = xfer->rx_len - rx_len; + rx_len += size; + + /* Receive payload */ + while (size >= 4) { + data = nx_mipi_dsi_read_fifo(index); + rxb[0] = (data >> 0) & 0xff; + rxb[1] = (data >> 8) & 0xff; + rxb[2] = (data >> 16) & 0xff; + rxb[3] = (data >> 24) & 0xff; + rxb += 4, size -= 4; + } + + if (size) { + data = nx_mipi_dsi_read_fifo(index); + switch (size) { + case 3: + rxb[2] = (data >> 16) & 0xff; + case 2: + rxb[1] = (data >> 8) & 0xff; + case 1: + rxb[0] = data & 0xff; + } + } + + if (rx_len == xfer->rx_len) + err = rx_len; + +clear_fifo: + size = DSI_RX_FIFO_SIZE / 4; + do { + data = nx_mipi_dsi_read_fifo(index); + if (data == DSI_RX_FIFO_EMPTY) + break; + } while (--size); + + return err; +} + +#define IS_SHORT(t) (9 > ((t) & 0x0f)) + +static int nx_mipi_transfer(struct mipi_dsi_device *dsi, + const struct mipi_dsi_msg *msg) +{ + struct mipi_xfer_msg xfer; + int err; + + if (!msg->tx_len) + return -EINVAL; + + /* set id */ + xfer.id = msg->type | (msg->channel << 6); + + /* short type msg */ + if (IS_SHORT(msg->type)) { + const char *txb = msg->tx_buf; + + if (msg->tx_len > 2) + return -EINVAL; + + xfer.tx_len = 0; /* no payload */ + xfer.data[0] = txb[0]; + xfer.data[1] = (msg->tx_len == 2) ? txb[1] : 0; + xfer.tx_buf = NULL; + } else { + xfer.tx_len = msg->tx_len; + xfer.data[0] = msg->tx_len & 0xff; + xfer.data[1] = msg->tx_len >> 8; + xfer.tx_buf = msg->tx_buf; + } + + xfer.rx_len = msg->rx_len; + xfer.rx_buf = msg->rx_buf; + xfer.flags = msg->flags; + + err = nx_mipi_transfer_tx(dsi, &xfer); + + if (xfer.rx_len) + err = nx_mipi_transfer_rx(dsi, &xfer); + + nx_mipi_transfer_done(dsi); + + return err; +} + +static ssize_t nx_mipi_write_buffer(struct mipi_dsi_device *dsi, + const void *data, size_t len) +{ + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .tx_buf = data, + .tx_len = len + }; + + switch (len) { + case 0: + return -EINVAL; + case 1: + msg.type = MIPI_DSI_DCS_SHORT_WRITE; + break; + case 2: + msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM; + break; + default: + msg.type = MIPI_DSI_DCS_LONG_WRITE; + break; + } + + if (dsi->mode_flags & MIPI_DSI_MODE_LPM) + msg.flags |= MIPI_DSI_MSG_USE_LPM; + + return nx_mipi_transfer(dsi, &msg); +} + +__weak int nx_mipi_dsi_lcd_bind(struct mipi_dsi_device *dsi) +{ + return 0; +} + +/* + * disply + * MIPI DSI Setting + * (1) Initiallize MIPI(DSIM,DPHY,PLL) + * (2) Initiallize LCD + * (3) ReInitiallize MIPI(DSIM only) + * (4) Turn on display(MLC,DPC,...) + */ +void nx_mipi_display(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_plane_top *top, struct dp_plane_info *planes, + struct dp_mipi_dev *dev) +{ + struct dp_plane_info *plane = planes; + struct mipi_dsi_device *dsi = &dev->dsi; + int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1; + int count = top->plane_num; + int i = 0, ret; + + printf("MIPI: dp.%d\n", module); + + /* map mipi-dsi write callback func */ + dsi->write_buffer = nx_mipi_write_buffer; + + ret = nx_mipi_dsi_lcd_bind(dsi); + if (ret) { + printf("Error: bind mipi-dsi lcd driver !\n"); + return; + } + + dp_control_init(module); + dp_plane_init(module); + + mipi_init(); + + /* set plane */ + dp_plane_screen_setup(module, top); + + for (i = 0; count > i; i++, plane++) { + if (!plane->enable) + continue; + dp_plane_layer_setup(module, plane); + dp_plane_layer_enable(module, plane, 1); + } + dp_plane_screen_enable(module, 1); + + /* set mipi */ + mipi_prepare(module, input, sync, ctrl, dev); + + if (dsi->ops && dsi->ops->prepare) + dsi->ops->prepare(dsi); + + if (dsi->ops && dsi->ops->enable) + dsi->ops->enable(dsi); + + mipi_enable(module, input, sync, ctrl, dev); + + /* set dp control */ + dp_control_setup(module, sync, ctrl); + dp_control_enable(module, 1); +} diff --git a/drivers/video/nexell/s5pxx18_dp_rgb.c b/drivers/video/nexell/s5pxx18_dp_rgb.c new file mode 100644 index 0000000..44e8edb --- /dev/null +++ b/drivers/video/nexell/s5pxx18_dp_rgb.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + */ + +#include <config.h> +#include <common.h> +#include <errno.h> + +#include <asm/arch/display.h> + +#include "soc/s5pxx18_soc_disptop.h" + +static int rgb_switch(int module, int input, struct dp_sync_info *sync, + struct dp_rgb_dev *dev) +{ + int mpu = dev->lcd_mpu_type; + int rsc = 0, sel = 0; + + switch (module) { + case 0: + sel = mpu ? 1 : 0; + break; + case 1: + sel = rsc ? 3 : 2; + break; + default: + printf("Fail, %s nuknown module %d\n", __func__, module); + return -1; + } + + nx_disp_top_set_primary_mux(sel); + return 0; +} + +void nx_rgb_display(int module, + struct dp_sync_info *sync, struct dp_ctrl_info *ctrl, + struct dp_plane_top *top, struct dp_plane_info *planes, + struct dp_rgb_dev *dev) +{ + struct dp_plane_info *plane = planes; + int input = module == 0 ? DP_DEVICE_DP0 : DP_DEVICE_DP1; + int count = top->plane_num; + int i = 0; + + printf("RGB: dp.%d\n", module); + + dp_control_init(module); + dp_plane_init(module); + + /* set plane */ + dp_plane_screen_setup(module, top); + + for (i = 0; count > i; i++, plane++) { + if (!plane->enable) + continue; + dp_plane_layer_setup(module, plane); + dp_plane_layer_enable(module, plane, 1); + } + + dp_plane_screen_enable(module, 1); + + rgb_switch(module, input, sync, dev); + + dp_control_setup(module, sync, ctrl); + dp_control_enable(module, 1); +} diff --git a/drivers/video/nexell_display.c b/drivers/video/nexell_display.c new file mode 100644 index 0000000..7ff4651 --- /dev/null +++ b/drivers/video/nexell_display.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Nexell Co., Ltd. + * + * Author: junghyun, kim jhkim@nexell.co.kr + * + * Copyright (C) 2019 Stefan Bosch stefan_b@posteo.net + */ + +#include <config.h> +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <malloc.h> +#include <linux/compat.h> +#include <linux/err.h> +#include <video.h> /* For struct video_uc_platdata */ +#include <fdtdec.h> +#include <fdt_support.h> +#include <video_fb.h> +#include <lcd.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/arch/display.h> +#include <asm/arch/display_dev.h> +#include "videomodes.h" + +DECLARE_GLOBAL_DATA_PTR; + +#if !defined(CONFIG_DM) && !defined(CONFIG_OF_CONTROL) +static struct nx_display_dev *dp_dev; +#endif + +static char *const dp_dev_str[] = { + [DP_DEVICE_RESCONV] = "RESCONV", + [DP_DEVICE_RGBLCD] = "LCD", + [DP_DEVICE_HDMI] = "HDMI", + [DP_DEVICE_MIPI] = "MiPi", + [DP_DEVICE_LVDS] = "LVDS", + [DP_DEVICE_CVBS] = "TVOUT", + [DP_DEVICE_DP0] = "DP0", + [DP_DEVICE_DP1] = "DP1", +}; + +#if CONFIG_IS_ENABLED(OF_CONTROL) +static void nx_display_parse_dp_sync(const void *blob, int node, + struct dp_sync_info *sync) +{ + sync->h_active_len = fdtdec_get_int(blob, node, "h_active_len", 0); + sync->h_sync_width = fdtdec_get_int(blob, node, "h_sync_width", 0); + sync->h_back_porch = fdtdec_get_int(blob, node, "h_back_porch", 0); + sync->h_front_porch = fdtdec_get_int(blob, node, "h_front_porch", 0); + sync->h_sync_invert = fdtdec_get_int(blob, node, "h_sync_invert", 0); + sync->v_active_len = fdtdec_get_int(blob, node, "v_active_len", 0); + sync->v_sync_width = fdtdec_get_int(blob, node, "v_sync_width", 0); + sync->v_back_porch = fdtdec_get_int(blob, node, "v_back_porch", 0); + sync->v_front_porch = fdtdec_get_int(blob, node, "v_front_porch", 0); + sync->v_sync_invert = fdtdec_get_int(blob, node, "v_sync_invert", 0); + sync->pixel_clock_hz = fdtdec_get_int(blob, node, "pixel_clock_hz", 0); + + debug("DP: sync ->\n"); + debug("ha:%d, hs:%d, hb:%d, hf:%d, hi:%d\n", + sync->h_active_len, sync->h_sync_width, + sync->h_back_porch, sync->h_front_porch, sync->h_sync_invert); + debug("va:%d, vs:%d, vb:%d, vf:%d, vi:%d\n", + sync->v_active_len, sync->v_sync_width, + sync->v_back_porch, sync->v_front_porch, sync->v_sync_invert); +} + +static void nx_display_parse_dp_ctrl(const void *blob, int node, + struct dp_ctrl_info *ctrl) +{ + /* clock gen */ + ctrl->clk_src_lv0 = fdtdec_get_int(blob, node, "clk_src_lv0", 0); + ctrl->clk_div_lv0 = fdtdec_get_int(blob, node, "clk_div_lv0", 0); + ctrl->clk_src_lv1 = fdtdec_get_int(blob, node, "clk_src_lv1", 0); + ctrl->clk_div_lv1 = fdtdec_get_int(blob, node, "clk_div_lv1", 0); + + /* scan format */ + ctrl->interlace = fdtdec_get_int(blob, node, "interlace", 0); + + /* syncgen format */ + ctrl->out_format = fdtdec_get_int(blob, node, "out_format", 0); + ctrl->invert_field = fdtdec_get_int(blob, node, "invert_field", 0); + ctrl->swap_RB = fdtdec_get_int(blob, node, "swap_RB", 0); + ctrl->yc_order = fdtdec_get_int(blob, node, "yc_order", 0); + + /* extern sync delay */ + ctrl->delay_mask = fdtdec_get_int(blob, node, "delay_mask", 0); + ctrl->d_rgb_pvd = fdtdec_get_int(blob, node, "d_rgb_pvd", 0); + ctrl->d_hsync_cp1 = fdtdec_get_int(blob, node, "d_hsync_cp1", 0); + ctrl->d_vsync_fram = fdtdec_get_int(blob, node, "d_vsync_fram", 0); + ctrl->d_de_cp2 = fdtdec_get_int(blob, node, "d_de_cp2", 0); + + /* extern sync delay */ + ctrl->vs_start_offset = + fdtdec_get_int(blob, node, "vs_start_offset", 0); + ctrl->vs_end_offset = fdtdec_get_int(blob, node, "vs_end_offset", 0); + ctrl->ev_start_offset = + fdtdec_get_int(blob, node, "ev_start_offset", 0); + ctrl->ev_end_offset = fdtdec_get_int(blob, node, "ev_end_offset", 0); + + /* pad clock seletor */ + ctrl->vck_select = fdtdec_get_int(blob, node, "vck_select", 0); + ctrl->clk_inv_lv0 = fdtdec_get_int(blob, node, "clk_inv_lv0", 0); + ctrl->clk_delay_lv0 = fdtdec_get_int(blob, node, "clk_delay_lv0", 0); + ctrl->clk_inv_lv1 = fdtdec_get_int(blob, node, "clk_inv_lv1", 0); + ctrl->clk_delay_lv1 = fdtdec_get_int(blob, node, "clk_delay_lv1", 0); + ctrl->clk_sel_div1 = fdtdec_get_int(blob, node, "clk_sel_div1", 0); + + debug("DP: ctrl [%s] ->\n", + ctrl->interlace ? "Interlace" : " Progressive"); + debug("cs0:%d, cd0:%d, cs1:%d, cd1:%d\n", + ctrl->clk_src_lv0, ctrl->clk_div_lv0, + ctrl->clk_src_lv1, ctrl->clk_div_lv1); + debug("fmt:0x%x, inv:%d, swap:%d, yb:0x%x\n", + ctrl->out_format, ctrl->invert_field, + ctrl->swap_RB, ctrl->yc_order); + debug("dm:0x%x, drp:%d, dhs:%d, dvs:%d, dde:0x%x\n", + ctrl->delay_mask, ctrl->d_rgb_pvd, + ctrl->d_hsync_cp1, ctrl->d_vsync_fram, ctrl->d_de_cp2); + debug("vss:%d, vse:%d, evs:%d, eve:%d\n", + ctrl->vs_start_offset, ctrl->vs_end_offset, + ctrl->ev_start_offset, ctrl->ev_end_offset); + debug("sel:%d, i0:%d, d0:%d, i1:%d, d1:%d, s1:%d\n", + ctrl->vck_select, ctrl->clk_inv_lv0, ctrl->clk_delay_lv0, + ctrl->clk_inv_lv1, ctrl->clk_delay_lv1, ctrl->clk_sel_div1); +} + +static void nx_display_parse_dp_top_layer(const void *blob, int node, + struct dp_plane_top *top) +{ + top->screen_width = fdtdec_get_int(blob, node, "screen_width", 0); + top->screen_height = fdtdec_get_int(blob, node, "screen_height", 0); + top->video_prior = fdtdec_get_int(blob, node, "video_prior", 0); + top->interlace = fdtdec_get_int(blob, node, "interlace", 0); + top->back_color = fdtdec_get_int(blob, node, "back_color", 0); + top->plane_num = DP_PLANS_NUM; + + debug("DP: top [%s] ->\n", + top->interlace ? "Interlace" : " Progressive"); + debug("w:%d, h:%d, prior:%d, bg:0x%x\n", + top->screen_width, top->screen_height, + top->video_prior, top->back_color); +} + +static void nx_display_parse_dp_layer(const void *blob, int node, + struct dp_plane_info *plane) +{ + plane->left = fdtdec_get_int(blob, node, "left", 0); + plane->width = fdtdec_get_int(blob, node, "width", 0); + plane->top = fdtdec_get_int(blob, node, "top", 0); + plane->height = fdtdec_get_int(blob, node, "height", 0); + plane->pixel_byte = fdtdec_get_int(blob, node, "pixel_byte", 0); + plane->format = fdtdec_get_int(blob, node, "format", 0); + plane->alpha_on = fdtdec_get_int(blob, node, "alpha_on", 0); + plane->alpha_depth = fdtdec_get_int(blob, node, "alpha", 0); + plane->tp_on = fdtdec_get_int(blob, node, "tp_on", 0); + plane->tp_color = fdtdec_get_int(blob, node, "tp_color", 0); + + /* enable layer */ + if (plane->fb_base) + plane->enable = 1; + else + plane->enable = 0; + + if (plane->fb_base == 0) { + printf("fail : dp plane.%d invalid fb base [0x%x] ->\n", + plane->layer, plane->fb_base); + return; + } + + debug("DP: plane.%d [0x%x] ->\n", plane->layer, plane->fb_base); + debug("f:0x%x, l:%d, t:%d, %d * %d, bpp:%d, a:%d(%d), t:%d(0x%x)\n", + plane->format, plane->left, plane->top, plane->width, + plane->height, plane->pixel_byte, plane->alpha_on, + plane->alpha_depth, plane->tp_on, plane->tp_color); +} + +static void nx_display_parse_dp_planes(const void *blob, int node, + struct nx_display_dev *dp, + struct video_uc_platdata *plat) +{ + const char *name; + + for (node = fdt_first_subnode(blob, node); + node > 0; node = fdt_next_subnode(blob, node)) { + name = fdt_get_name(blob, node, NULL); + + if (strcmp(name, "layer_top") == 0) + nx_display_parse_dp_top_layer(blob, node, &dp->top); + + /* + * TODO: Is it sure that only one layer is used? Otherwise + * fb_base must be different? + */ + if (strcmp(name, "layer_0") == 0) { + dp->planes[0].fb_base = + (uint)map_sysmem(plat->base, plat->size); + debug("%s(): dp->planes[0].fb_base == 0x%x\n", __func__, + (uint)dp->planes[0].fb_base); + nx_display_parse_dp_layer(blob, node, &dp->planes[0]); + } + + if (strcmp(name, "layer_1") == 0) { + dp->planes[1].fb_base = + (uint)map_sysmem(plat->base, plat->size); + debug("%s(): dp->planes[1].fb_base == 0x%x\n", __func__, + (uint)dp->planes[1].fb_base); + nx_display_parse_dp_layer(blob, node, &dp->planes[1]); + } + + if (strcmp(name, "layer_2") == 0) { + dp->planes[2].fb_base = + (uint)map_sysmem(plat->base, plat->size); + debug("%s(): dp->planes[2].fb_base == 0x%x\n", __func__, + (uint)dp->planes[2].fb_base); + nx_display_parse_dp_layer(blob, node, &dp->planes[2]); + } + } +} + +static int nx_display_parse_dp_lvds(const void *blob, int node, + struct nx_display_dev *dp) +{ + struct dp_lvds_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (!dev) { + printf("failed to allocate display LVDS object.\n"); + return -ENOMEM; + } + + dp->device = dev; + + dev->lvds_format = fdtdec_get_int(blob, node, "format", 0); + dev->pol_inv_hs = fdtdec_get_int(blob, node, "pol_inv_hs", 0); + dev->pol_inv_vs = fdtdec_get_int(blob, node, "pol_inv_vs", 0); + dev->pol_inv_de = fdtdec_get_int(blob, node, "pol_inv_de", 0); + dev->pol_inv_ck = fdtdec_get_int(blob, node, "pol_inv_ck", 0); + dev->voltage_level = fdtdec_get_int(blob, node, "voltage_level", 0); + + if (!dev->voltage_level) + dev->voltage_level = DEF_VOLTAGE_LEVEL; + + debug("DP: LVDS -> %s, voltage LV:0x%x\n", + dev->lvds_format == DP_LVDS_FORMAT_VESA ? "VESA" : + dev->lvds_format == DP_LVDS_FORMAT_JEIDA ? "JEIDA" : "LOC", + dev->voltage_level); + debug("pol inv hs:%d, vs:%d, de:%d, ck:%d\n", + dev->pol_inv_hs, dev->pol_inv_vs, + dev->pol_inv_de, dev->pol_inv_ck); + + return 0; +} + +static int nx_display_parse_dp_rgb(const void *blob, int node, + struct nx_display_dev *dp) +{ + struct dp_rgb_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (!dev) { + printf("failed to allocate display RGB LCD object.\n"); + return -ENOMEM; + } + dp->device = dev; + + dev->lcd_mpu_type = fdtdec_get_int(blob, node, "lcd_mpu_type", 0); + + debug("DP: RGB -> MPU[%s]\n", dev->lcd_mpu_type ? "O" : "X"); + return 0; +} + +static int nx_display_parse_dp_mipi(const void *blob, int node, + struct nx_display_dev *dp) +{ + struct dp_mipi_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (!dev) { + printf("failed to allocate display MiPi object.\n"); + return -ENOMEM; + } + dp->device = dev; + + dev->lp_bitrate = fdtdec_get_int(blob, node, "lp_bitrate", 0); + dev->hs_bitrate = fdtdec_get_int(blob, node, "hs_bitrate", 0); + dev->lpm_trans = 1; + dev->command_mode = 0; + + debug("DP: MIPI ->\n"); + debug("lp:%dmhz, hs:%dmhz\n", dev->lp_bitrate, dev->hs_bitrate); + + return 0; +} + +static int nx_display_parse_dp_hdmi(const void *blob, int node, + struct nx_display_dev *dp) +{ + struct dp_hdmi_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (!dev) { + printf("failed to allocate display HDMI object.\n"); + return -ENOMEM; + } + dp->device = dev; + + dev->preset = fdtdec_get_int(blob, node, "preset", 0); + + debug("DP: HDMI -> %d\n", dev->preset); + + return 0; +} + +static int nx_display_parse_dp_lcds(const void *blob, int node, + const char *type, struct nx_display_dev *dp) +{ + if (strcmp(type, "lvds") == 0) { + dp->dev_type = DP_DEVICE_LVDS; + return nx_display_parse_dp_lvds(blob, node, dp); + } else if (strcmp(type, "rgb") == 0) { + dp->dev_type = DP_DEVICE_RGBLCD; + return nx_display_parse_dp_rgb(blob, node, dp); + } else if (strcmp(type, "mipi") == 0) { + dp->dev_type = DP_DEVICE_MIPI; + return nx_display_parse_dp_mipi(blob, node, dp); + } else if (strcmp(type, "hdmi") == 0) { + dp->dev_type = DP_DEVICE_HDMI; + return nx_display_parse_dp_hdmi(blob, node, dp); + } + + printf("%s: node %s unknown display type\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + + return 0; +} + +#define DT_SYNC (1 << 0) +#define DT_CTRL (1 << 1) +#define DT_PLANES (1 << 2) +#define DT_DEVICE (1 << 3) + +static int nx_display_parse_dt(const void *blob, + struct nx_display_dev *dp, int node, + struct video_uc_platdata *plat) +{ + const char *name, *dtype; + int ret = 0; + unsigned int dt_status = 0; + + if (!node) + return -ENODEV; + + dp->module = fdtdec_get_int(blob, node, "module", -1); + if (dp->module == -1) + dp->module = fdtdec_get_int(blob, node, "index", 0); + + dtype = fdt_getprop(blob, node, "lcd-type", NULL); + + for (node = fdt_first_subnode(blob, node); + node > 0; node = fdt_next_subnode(blob, node)) { + name = fdt_get_name(blob, node, NULL); + + if (strcmp("dp-sync", name) == 0) { + dt_status |= DT_SYNC; + nx_display_parse_dp_sync(blob, node, &dp->sync); + } + + if (strcmp("dp-ctrl", name) == 0) { + dt_status |= DT_CTRL; + nx_display_parse_dp_ctrl(blob, node, &dp->ctrl); + } + + if (strcmp("dp-planes", name) == 0) { + dt_status |= DT_PLANES; + nx_display_parse_dp_planes(blob, node, dp, plat); + } + + if (strcmp("dp-device", name) == 0) { + dt_status |= DT_DEVICE; + ret = nx_display_parse_dp_lcds(blob, node, dtype, dp); + } + } + + if (dt_status != (DT_SYNC | DT_CTRL | DT_PLANES | DT_DEVICE)) { + printf("Not enough DT config for display [0x%x]\n", dt_status); + return -ENODEV; + } + + return ret; +} +#endif + +__weak int nx_display_fixup_dp(struct nx_display_dev *dp) +{ + return 0; +} + +static struct nx_display_dev *nx_display_setup(void) +{ + struct nx_display_dev *dp; + int i, ret; + int node = 0; + struct video_uc_platdata *plat = NULL; + + struct udevice *dev; + + /* call driver probe */ + debug("DT: uclass device call...\n"); + + ret = uclass_get_device(UCLASS_VIDEO, 0, &dev); + if (ret) { + debug("%s(): uclass_get_device(UCLASS_VIDEO, 0, &dev) != 0 --> return NULL\n", __func__); + return NULL; + } + plat = dev_get_uclass_platdata(dev); + if (!dev) { + debug("%s(): dev_get_uclass_platdata(dev) == NULL --> return NULL\n", __func__); + return NULL; + } + dp = dev_get_priv(dev); + if (!dp) { + debug("%s(): dev_get_priv(dev) == NULL --> return NULL\n", __func__); + return NULL; + } + node = dev->node.of_offset; + +#if CONFIG_IS_ENABLED(OF_CONTROL) + ret = nx_display_parse_dt(gd->fdt_blob, dp, node, plat); + if (ret) + goto err_setup; +#endif + + nx_display_fixup_dp(dp); + + for (i = 0; dp->top.plane_num > i; i++) { + dp->planes[i].layer = i; + if (dp->planes[i].enable && !dp->fb_plane) { + dp->fb_plane = &dp->planes[i]; + dp->fb_addr = dp->fb_plane->fb_base; + dp->depth = dp->fb_plane->pixel_byte; + } + } + + switch (dp->dev_type) { +#ifdef CONFIG_VIDEO_NX_RGB + case DP_DEVICE_RGBLCD: + nx_rgb_display(dp->module, + &dp->sync, &dp->ctrl, &dp->top, + dp->planes, (struct dp_rgb_dev *)dp->device); + break; +#endif +#ifdef CONFIG_VIDEO_NX_LVDS + case DP_DEVICE_LVDS: + nx_lvds_display(dp->module, + &dp->sync, &dp->ctrl, &dp->top, + dp->planes, (struct dp_lvds_dev *)dp->device); + break; +#endif +#ifdef CONFIG_VIDEO_NX_MIPI + case DP_DEVICE_MIPI: + nx_mipi_display(dp->module, + &dp->sync, &dp->ctrl, &dp->top, + dp->planes, (struct dp_mipi_dev *)dp->device); + break; +#endif +#ifdef CONFIG_VIDEO_NX_HDMI + case DP_DEVICE_HDMI: + nx_hdmi_display(dp->module, + &dp->sync, &dp->ctrl, &dp->top, + dp->planes, (struct dp_hdmi_dev *)dp->device); + break; +#endif + default: + printf("fail : not support lcd type %d !!!\n", dp->dev_type); + goto err_setup; + }; + + printf("LCD: [%s] dp.%d.%d %dx%d %dbpp FB:0x%08x\n", + dp_dev_str[dp->dev_type], dp->module, dp->fb_plane->layer, + dp->fb_plane->width, dp->fb_plane->height, dp->depth * 8, + dp->fb_addr); + + return dp; + +err_setup: + kfree(dp); + + return NULL; +} + +#if defined CONFIG_LCD + +/* default lcd */ +struct vidinfo panel_info = { + .vl_col = 320, .vl_row = 240, .vl_bpix = 32, +}; + +void lcd_ctrl_init(void *lcdbase) +{ + vidinfo_t *pi = &panel_info; + struct nx_display_dev *dp; + int bpix; + + dp = nx_display_setup(); + if (!dp) + return NULL; + + switch (dp->depth) { + case 2: + bpix = LCD_COLOR16; + break; + case 3: + case 4: + bpix = LCD_COLOR32; + break; + default: + printf("fail : not support LCD bit per pixel %d\n", + dp->depth * 8); + return NULL; + } + + dp->panel_info = pi; + + /* set resolution with config */ + pi->vl_bpix = bpix; + pi->vl_col = dp->fb_plane->width; + pi->vl_row = dp->fb_plane->height; + pi->priv = dp; + gd->fb_base = dp->fb_addr; +} + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +} + +__weak void lcd_enable(void) +{ +} +#endif + +static int nx_display_dev_probe_uclass(struct udevice *dev) +{ + struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + struct nx_display_platdata *plat = dev_get_platdata(dev); + static GraphicDevice *graphic_device; + char addr[64]; + + debug("%s()\n", __func__); + + if (!dev) + return -EINVAL; + + if (!uc_plat) { + debug("%s(): video_uc_platdata *plat == NULL --> return -EINVAL\n", __func__); + return -EINVAL; + } + + if (!uc_priv) { + debug("%s(): video_priv *uc_priv == NULL --> return -EINVAL\n", __func__); + return -EINVAL; + } + + if (!plat) { + debug("%s(): nx_display_platdata *plat == NULL --> return -EINVAL\n", __func__); + return -EINVAL; + } + + struct nx_display_dev *dp; + unsigned int pp_index = 0; + + dp = nx_display_setup(); + if (!dp) { + debug("%s(): nx_display_setup() == 0 --> return -EINVAL\n", __func__); + return -EINVAL; + } + + switch (dp->depth) { + case 2: + pp_index = GDF_16BIT_565RGB; + uc_priv->bpix = VIDEO_BPP16; + break; + case 3: + /* There is no VIDEO_BPP24 because these values are of + * type video_log2_bpp + */ + case 4: + pp_index = GDF_32BIT_X888RGB; + uc_priv->bpix = VIDEO_BPP32; + break; + default: + printf("fail : not support LCD bit per pixel %d\n", + dp->depth * 8); + return -EINVAL; + } + + uc_priv->xsize = dp->fb_plane->width; + uc_priv->ysize = dp->fb_plane->height; + uc_priv->rot = 0; + + graphic_device = &dp->graphic_device; + graphic_device->frameAdrs = dp->fb_addr; + graphic_device->gdfIndex = pp_index; + graphic_device->gdfBytesPP = dp->depth; + graphic_device->winSizeX = dp->fb_plane->width; + graphic_device->winSizeY = dp->fb_plane->height; + graphic_device->plnSizeX = + graphic_device->winSizeX * graphic_device->gdfBytesPP; + + /* + * set environment variable "fb_addr" (frame buffer address), required + * for splash image. Because drv_video_init() in common/stdio.c is only + * called when CONFIG_VIDEO is set (and not when CONFIG_DM_VIDEO is set). + */ + sprintf(addr, "0x%x", dp->fb_addr); + debug("%s(): env_set("fb_addr", %s) ...\n", __func__, addr); + env_set("fb_addr", addr); + + return 0; +} + +static int nx_display_bind(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + + debug("%s()\n", __func__); + + /* Datasheet S5p4418: + * Resolution up to 2048 x 1280, up to 12 Bit per color (HDMI) + * Actual (max.) size is 0x1000000 because in U-Boot nanopi2-2016.01 + * "#define CONFIG_FB_ADDR 0x77000000" and next address is + * "#define BMP_LOAD_ADDR 0x78000000" + */ + plat->size = 0x1000000; + + return 0; +} + +static int nx_display_probe(struct udevice *dev) +{ + return nx_display_dev_probe_uclass(dev); +} + +static const struct udevice_id nx_display_ids[] = { + {.compatible = "nexell,nexell-display", }, + {} +}; + +U_BOOT_DRIVER(nexell_display) = { + .name = "nexell-display", + .id = UCLASS_VIDEO, + .of_match = nx_display_ids, + .platdata_auto_alloc_size = + sizeof(struct nx_display_platdata), + .bind = nx_display_bind, + .probe = nx_display_probe, + .priv_auto_alloc_size = sizeof(struct nx_display_dev), +};

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01: - SPL not supported yet --> no spl-dir in arch/arm/cpu/armv7/s5p4418/. Appropriate line in Makefile removed. - cpu.c: '#include <cpu_func.h>' added. - arch/arm/cpu/armv7/s5p4418/u-boot.lds removed, is not required anylonger. - s5p4418.dtsi: '#include "../../../include/generated/autoconf.h"' removed, is not necessary, error at out-of-tree building. '#ifdef CONFIG_CPU_NXP4330'-blocks (2x) removed. Some minor changes regarding mmc. 'u-boot,dm-pre-reloc' added to dp0 because of added DM_VIDEO support. - board/s5p4418/ renamed to board/friendlyarm/ - All s5p4418-boards except nanopi2 removed because there is no possibility to test the other boards. - Kconfig: Changes to have a structure like mach-bcm283x (RaspberryPi), e.g. "config ..." entries moved from/to other Kconfig. - "CONFIG_" removed from several s5p4418/nanopi2 specific defines because the appropriate values do not need to be configurable. - nanopi2/board.c: All getenv(), getenv_ulong(), setenv() and saveenv() renamed to env_get(), env_get_ulong(), env_set() and env_save(), respectively. MACH_TYPE_S5P4418 is not defined anymore, therefore appropriate code removed (not necessary for DT-kernels). - nanopi2/onewire.c: All crc8() renamed to crc8_ow() because crc8() is already defined in lib/crc8.c (with different parameters).
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
arch/arm/cpu/armv7/Makefile | 1 + arch/arm/cpu/armv7/s5p4418/Makefile | 6 + arch/arm/cpu/armv7/s5p4418/cpu.c | 120 ++++++ arch/arm/dts/Makefile | 3 + arch/arm/dts/s5p4418-nanopi2.dts | 109 ++++++ arch/arm/dts/s5p4418-pinctrl.dtsi | 66 ++++ arch/arm/dts/s5p4418.dtsi | 157 ++++++++ board/friendlyarm/Kconfig | 39 ++ board/friendlyarm/nanopi2/Kconfig | 12 + board/friendlyarm/nanopi2/MAINTAINERS | 7 + board/friendlyarm/nanopi2/Makefile | 6 + board/friendlyarm/nanopi2/board.c | 581 ++++++++++++++++++++++++++++ board/friendlyarm/nanopi2/hwrev.c | 122 ++++++ board/friendlyarm/nanopi2/hwrev.h | 29 ++ board/friendlyarm/nanopi2/lcds.c | 703 ++++++++++++++++++++++++++++++++++ board/friendlyarm/nanopi2/nxp-fb.h | 99 +++++ board/friendlyarm/nanopi2/onewire.c | 323 ++++++++++++++++ board/friendlyarm/nanopi2/onewire.h | 29 ++ 18 files changed, 2412 insertions(+) create mode 100644 arch/arm/cpu/armv7/s5p4418/Makefile create mode 100644 arch/arm/cpu/armv7/s5p4418/cpu.c create mode 100644 arch/arm/dts/s5p4418-nanopi2.dts create mode 100644 arch/arm/dts/s5p4418-pinctrl.dtsi create mode 100644 arch/arm/dts/s5p4418.dtsi create mode 100644 board/friendlyarm/Kconfig create mode 100644 board/friendlyarm/nanopi2/Kconfig create mode 100644 board/friendlyarm/nanopi2/MAINTAINERS create mode 100644 board/friendlyarm/nanopi2/Makefile create mode 100644 board/friendlyarm/nanopi2/board.c create mode 100644 board/friendlyarm/nanopi2/hwrev.c create mode 100644 board/friendlyarm/nanopi2/hwrev.h create mode 100644 board/friendlyarm/nanopi2/lcds.c create mode 100644 board/friendlyarm/nanopi2/nxp-fb.h create mode 100644 board/friendlyarm/nanopi2/onewire.c create mode 100644 board/friendlyarm/nanopi2/onewire.h
diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index 8c955d0..ee87417 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_RMOBILE) += rmobile/ obj-$(if $(filter stv0991,$(SOC)),y) += stv0991/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_VF610) += vf610/ +obj-$(CONFIG_ARCH_S5P4418) += s5p4418/ diff --git a/arch/arm/cpu/armv7/s5p4418/Makefile b/arch/arm/cpu/armv7/s5p4418/Makefile new file mode 100644 index 0000000..321b257 --- /dev/null +++ b/arch/arm/cpu/armv7/s5p4418/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2016 Nexell +# Hyunseok, Jung hsjung@nexell.co.kr + +obj-y += cpu.o diff --git a/arch/arm/cpu/armv7/s5p4418/cpu.c b/arch/arm/cpu/armv7/s5p4418/cpu.c new file mode 100644 index 0000000..79c284f --- /dev/null +++ b/arch/arm/cpu/armv7/s5p4418/cpu.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Hyunseok, Jung hsjung@nexell.co.kr + */ + +#include <common.h> +#include <command.h> +#include <asm/system.h> +#include <asm/cache.h> +#include <asm/sections.h> +#include <asm/io.h> +#include <asm/arch/nexell.h> +#include <asm/arch/clk.h> +#include <asm/arch/reset.h> +#include <asm/arch/tieoff.h> +#include <cpu_func.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_ARCH_CPU_INIT +#error must be define the macro "CONFIG_ARCH_CPU_INIT" +#endif + +void s_init(void) +{ +} + +static void cpu_soc_init(void) +{ + /* + * NOTE> ALIVE Power Gate must enable for Alive register access. + * must be clear wfi jump address + */ + writel(1, ALIVEPWRGATEREG); + writel(0xFFFFFFFF, SCR_ARM_SECOND_BOOT); + + /* write 0xf0 on alive scratchpad reg for boot success check */ + writel(readl(SCR_SIGNAGURE_READ) | 0xF0, (SCR_SIGNAGURE_SET)); + + /* set l2 cache tieoff */ + nx_tieoff_set(NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2RET1N_0, 1); + nx_tieoff_set(NX_TIEOFF_CORTEXA9MP_TOP_QUADL2C_L2RET1N_1, 1); +} + +#ifdef CONFIG_PL011_SERIAL +static void serial_device_init(void) +{ + char dev[10]; + int id; + + sprintf(dev, "nx-uart.%d", CONFIG_CONS_INDEX); + id = RESET_ID_UART0 + CONFIG_CONS_INDEX; + + struct clk *clk = clk_get((const char *)dev); + + /* reset control: Low active ___|--- */ + nx_rstcon_setrst(id, RSTCON_ASSERT); + udelay(10); + nx_rstcon_setrst(id, RSTCON_NEGATE); + udelay(10); + + /* set clock */ + clk_disable(clk); + clk_set_rate(clk, CONFIG_PL011_CLOCK); + clk_enable(clk); +} +#endif + +int arch_cpu_init(void) +{ + flush_dcache_all(); + cpu_soc_init(); + clk_init(); + +#ifdef CONFIG_PL011_SERIAL + serial_device_init(); +#endif + return 0; +} + +#if defined(CONFIG_DISPLAY_CPUINFO) +int print_cpuinfo(void) +{ + return 0; +} +#endif + +void reset_cpu(ulong ignored) +{ + void *clkpwr_reg = (void *)PHY_BASEADDR_CLKPWR; + const u32 sw_rst_enb_bitpos = 3; + const u32 sw_rst_enb_mask = 1 << sw_rst_enb_bitpos; + const u32 sw_rst_bitpos = 12; + const u32 sw_rst_mask = 1 << sw_rst_bitpos; + int pwrcont = 0x224; + int pwrmode = 0x228; + u32 read_value; + + read_value = readl((void *)(clkpwr_reg + pwrcont)); + + read_value &= ~sw_rst_enb_mask; + read_value |= 1 << sw_rst_enb_bitpos; + + writel(read_value, (void *)(clkpwr_reg + pwrcont)); + writel(sw_rst_mask, (void *)(clkpwr_reg + pwrmode)); +} + +void enable_caches(void) +{ + /* Enable D-cache. I-cache is already enabled in start.S */ + dcache_enable(); +} + +#if defined(CONFIG_ARCH_MISC_INIT) +int arch_misc_init(void) +{ + return 0; +} +#endif /* CONFIG_ARCH_MISC_INIT */ diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 04a8ccc..065b2d2 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -137,6 +137,9 @@ dtb-$(CONFIG_ROCKCHIP_RV1108) += \ rv1108-elgin-r1.dtb \ rv1108-evb.dtb
+dtb-$(CONFIG_ARCH_S5P4418) += \ + s5p4418-nanopi2.dtb + dtb-$(CONFIG_ARCH_MESON) += \ meson-gxbb-nanopi-k2.dtb \ meson-gxbb-odroidc2.dtb \ diff --git a/arch/arm/dts/s5p4418-nanopi2.dts b/arch/arm/dts/s5p4418-nanopi2.dts new file mode 100644 index 0000000..4fb3097 --- /dev/null +++ b/arch/arm/dts/s5p4418-nanopi2.dts @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2017 FriendlyElec Computer Tech. Co., Ltd. + * (http://www.friendlyarm.com) + * + * (C) Copyright 2016 Nexell + * Youngbok, Park park@nexell.co.kr + */ + +/dts-v1/; +#include "s5p4418.dtsi" + +/ { + model = "FriendlyElec boards based on Nexell s5p4418"; + cpu-model = "S5p4418"; + + compatible = "friendlyelec,nanopi2", + "nexell,s5p4418"; + + aliases { + mmc0 = "/mmc@c0069000"; + mmc1 = "/mmc@c0062000"; + i2c2 = "/i2c@2"; + }; + + mmc0:mmc@c0062000 { + frequency = <50000000>; + nexell,drive_dly = <0x0>; + nexell,drive_shift = <0x03>; + nexell,sample_dly = <0x00>; + nexell,sample_shift = <0x02>; + status = "okay"; + }; + + mmc2:mmc@c0069000 { + frequency = <50000000>; + nexell,drive_dly = <0x0>; + nexell,drive_shift = <0x03>; + nexell,sample_dly = <0x00>; + nexell,sample_shift = <0x02>; + nexell,bus-width = <4>; + index = <2>; + status = "okay"; + }; + + ehci:usbhost@c0030000 { + status = "okay"; + }; + + dwc2otg@c0040000 { + status = "okay"; + }; + + i2c2:i2c@2 { + compatible = "i2c-gpio"; + #address-cells = <1>; + #size-cells = <0>; + gpios = <&gpio_d 7 0>, /* SDA */ + <&gpio_d 6 0>; /* SCL */ + i2c-gpio,delay-us = <3>; + status ="okay"; + }; + + dp0:dp@c0102800 { + status = "okay"; + module = <0>; + lcd-type = "lvds"; + + dp-device { + format = <0>; /* 0:VESA, 1:JEIDA */ + }; + + dp-sync { + h_active_len = <1024>; + h_front_porch = <84>; + h_back_porch = <84>; + h_sync_width = <88>; + h_sync_invert = <0>; + v_active_len = <600>; + v_front_porch = <10>; + v_back_porch = <10>; + v_sync_width = <20>; + v_sync_invert = <0>; + }; + + dp-ctrl { + clk_src_lv0 = <3>; + clk_div_lv0 = <16>; + clk_src_lv1 = <7>; + clk_div_lv1 = <1>; + out_format = <2>; + }; + + dp-planes { + layer_top { + screen_width = <1024>; + screen_height = <600>; + back_color = <0x0>; + }; + + layer_1 { /* RGB 1 */ + width = <1024>; + height = <600>; + format = <0x06530000>; + pixel_byte = <4>; + }; + }; + }; +}; diff --git a/arch/arm/dts/s5p4418-pinctrl.dtsi b/arch/arm/dts/s5p4418-pinctrl.dtsi new file mode 100644 index 0000000..dcf745a --- /dev/null +++ b/arch/arm/dts/s5p4418-pinctrl.dtsi @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Nexell's s5p6818 SoC pin-mux and pin-config device tree source + * + * Copyright (C) 2016 Nexell Co., Ltd. + * http://www.nexell.co.kr + * + * Nexell's s5p6818 SoC pin-mux and pin-config options are listed as + * device tree nodes in this file. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +pinctrl@C0010000 { + /* GMAC */ + gmac_txd: gmac-txd { + nexell,pins = "gpioe-7", "gpioe-8", "gpioe-9", "gpioe-10"; + nexell,pin-function = <1>; + nexell,pin-pull = <2>; + nexell,pin-strength = <3>; + }; + + gmac_rxd: gmac-rxd { + nexell,pins = "gpioe-14", "gpioe-15", "gpioe-16", "gpioe-17"; + nexell,pin-function = <1>; + nexell,pin-pull = <2>; + nexell,pin-strength = <3>; + }; + + gmac_txen: gmac-txen { + nexell,pins = "gpioe-11"; + nexell,pin-function = <1>; + nexell,pin-pull = <2>; + nexell,pin-strength = <3>; + }; + + gmac_mdc: gmac-mdc { + nexell,pins = "gpioe-20"; + nexell,pin-function = <1>; + nexell,pin-pull = <2>; + nexell,pin-strength = <3>; + }; + + gmac_mdio: gmac-mdio { + nexell,pins = "gpioe-21"; + nexell,pin-function = <1>; + nexell,pin-pull = <2>; + nexell,pin-strength = <3>; + }; + + gmac_rxclk: gmac-rxclk { + nexell,pins = "gpioe-18"; + nexell,pin-function = <1>; + nexell,pin-pull = <2>; + nexell,pin-strength = <3>; + }; + + gmac_txclk: gmac-txclk { + nexell,pins = "gpioe-24"; + nexell,pin-function = <1>; + nexell,pin-pull = <2>; + nexell,pin-strength = <2>; + }; +}; diff --git a/arch/arm/dts/s5p4418.dtsi b/arch/arm/dts/s5p4418.dtsi new file mode 100644 index 0000000..d090d9b --- /dev/null +++ b/arch/arm/dts/s5p4418.dtsi @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Nexell + * Youngbok, Park park@nexell.co.kr + */ + +#include "skeleton.dtsi" + +/ { + #include "s5p4418-pinctrl.dtsi" + aliases { + mmc0 = &mmc0; + mmc1 = &mmc1; + mmc2 = &mmc2; + gmac = "/ethernet@c0060000"; + }; + + mmc2:mmc@c0069000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "nexell,nexell-dwmmc"; + reg = <0xc0069000 0x1000>; + nexell,bus-width = <4>; + index = <2>; + max-frequency = <50000000>; + status = "disabled"; + }; + + mmc1:mmc@c0068000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "nexell,nexell-dwmmc"; + reg = <0xc0068000 0x1000>; + nexell,bus-width = <4>; + index = <1>; + max-frequency = <50000000>; + status = "disabled"; + }; + + mmc0:mmc@c0062000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "nexell,nexell-dwmmc"; + reg = <0xc0062000 0x1000>; + nexell,bus-width = <4>; + index = <0>; + max-frequency = <50000000>; + status = "disabled"; + }; + + gmac:ethernet@c0060000 { + compatible = "nexell,nexell-gmac"; + reg = <0xc0060000 0x8000>; + phy-mode = "rgmii"; + pinctrl-names = "default"; + pinctrl-0 = <&gmac_txd>, <&gmac_rxd>, <&gmac_txen>, + <&gmac_mdc>, <&gmac_mdio>, <&gmac_rxclk>, <&gmac_txclk>; + status = "disabled"; + }; + + ehci:usbhost@c0030000 { + compatible = "nexell,nexell-ehci"; + reg = <0xC0030000 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + phy { + compatible = "samsung,exynos-usb-phy"; + reg = <0xC0011000 0x100>; + }; + }; + + dwc2otg@c0040000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "nexell,dwc2"; + reg = <0xC0011000 0x100>, <0xC0040000 0x11000>; + status = "disabled"; + }; + + dp0:dp@c0102800 { + compatible = "nexell,nexell-display"; + reg = <0xc0102800 0x100>; + index = <0>; + u-boot,dm-pre-reloc; + status = "disabled"; + }; + + dp1:dp@c0102c00 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "nexell,nexell-display"; + reg = <0xc0102c00 0x100>; + index = <1>; + status = "disabled"; + }; + + gpio_a:gpio@c001a000 { + compatible = "nexell,nexell-gpio"; + reg = <0xc001a000 0x00000010>; + altr,gpio-bank-width = <32>; + gpio-bank-name = "gpio_a"; + gpio-controller; + #gpio-cells = <2>; + }; + + gpio_b:gpio@c001b000 { + compatible = "nexell,nexell-gpio"; + reg = <0xc001b000 0x00000010>; + altr,gpio-bank-width = <32>; + gpio-bank-name = "gpio_b"; + gpio-controller; + #gpio-cells = <2>; + }; + + gpio_c:gpio@c001c000 { + compatible = "nexell,nexell-gpio"; + reg = <0xc001c000 0x00000010>; + nexell,gpio-bank-width = <32>; + gpio-bank-name = "gpio_c"; + gpio-controller; + #gpio-cells = <2>; + }; + + gpio_d:gpio@c001d000 { + compatible = "nexell,nexell-gpio"; + reg = <0xc001d000 0x00000010>; + nexell,gpio-bank-width = <32>; + gpio-bank-name = "gpio_d"; + gpio-controller; + #gpio-cells = <2>; + }; + + gpio_e:gpio@c001e000 { + compatible = "nexell,nexell-gpio"; + reg = <0xc001e000 0x00000010>; + nexell,gpio-bank-width = <32>; + gpio-bank-name = "gpio_e"; + gpio-controller; + #gpio-cells = <2>; + }; + + gpio_alv:gpio@c0010800 { + compatible = "nexell,nexell-gpio"; + reg = <0xc0010800 0x00000010>; + nexell,gpio-bank-width = <32>; + gpio-bank-name = "gpio_alv"; + gpio-controller; + #gpio-cells = <2>; + }; + + pinctrl@C0010000 { + compatible = "nexell,s5pxx18-pinctrl"; + reg = <0xc0010000 0xf000>; + u-boot,dm-pre-reloc; + }; +}; diff --git a/board/friendlyarm/Kconfig b/board/friendlyarm/Kconfig new file mode 100644 index 0000000..8f79640 --- /dev/null +++ b/board/friendlyarm/Kconfig @@ -0,0 +1,39 @@ + +#config SYS_CONFIG_NAME +# string "Board header file" +# help +# This option should contain the base name of board header file. +# The header file include/configs/<CONFIG_SYS_CONFIG_NAME>.h +# should be included from include/config.h. + +config S5P4418_ONEWIRE + bool "S5P4418_ONEWIRE" + #default y + +config PWM_NX + bool "PWM_NX" + #default y + +config SYS_RESERVE_MEM_SIZE + hex "SYS_RESERVE_MEM_SIZE" + +config ROOT_DEV + int "ROOT_DEV" + help + Environment variable rootdev is set to this value if env. var. firstboot + does not exist. Otherwise rootdev is set to the MMC boot device. rootdev + determines (together with env. var. bootpart) where the OS (linux) is + booted from. + +config BOOT_PART + int "BOOT_PART" + help + Environment variable bootpart is set to this value. bootpart determines + (together with env. var. rootdev) where the OS (linux) is booted from. + +config ROOT_PART + int "ROOT_PART" + help + Environment variable rootpart is set to this value. + +source "board/friendlyarm/nanopi2/Kconfig" diff --git a/board/friendlyarm/nanopi2/Kconfig b/board/friendlyarm/nanopi2/Kconfig new file mode 100644 index 0000000..0f68422 --- /dev/null +++ b/board/friendlyarm/nanopi2/Kconfig @@ -0,0 +1,12 @@ +if TARGET_NANOPI2 + +config SYS_BOARD + default "nanopi2" + +config SYS_VENDOR + default "friendlyarm" + +config SYS_CONFIG_NAME + default "s5p4418_nanopi2" + +endif diff --git a/board/friendlyarm/nanopi2/MAINTAINERS b/board/friendlyarm/nanopi2/MAINTAINERS new file mode 100644 index 0000000..c8e2ce7 --- /dev/null +++ b/board/friendlyarm/nanopi2/MAINTAINERS @@ -0,0 +1,7 @@ +NANOPI2 BOARD +NANOPC-T2 BOARD +M: Stefan Bosch stefan_b@posteo.net +S: Maintained +F: board/s5p4418/nanopi2/ +F: include/configs/s5p4418_nanopi2.h +F: configs/s5p4418_nanopi2_defconfig diff --git a/board/friendlyarm/nanopi2/Makefile b/board/friendlyarm/nanopi2/Makefile new file mode 100644 index 0000000..cd459f2 --- /dev/null +++ b/board/friendlyarm/nanopi2/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2016 Nexell +# Hyunseok, Jung hsjung@nexell.co.kr + +obj-y := board.o hwrev.o onewire.o lcds.o diff --git a/board/friendlyarm/nanopi2/board.c b/board/friendlyarm/nanopi2/board.c new file mode 100644 index 0000000..91fd206 --- /dev/null +++ b/board/friendlyarm/nanopi2/board.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd. + * (http://www.friendlyarm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +#include <config.h> +#include <common.h> +#ifdef CONFIG_PWM_NX +#include <pwm.h> +#endif +#include <asm/io.h> + +#include <asm/arch/nexell.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/display.h> +#include <asm/arch/display_dev.h> + +#include <u-boot/md5.h> + +#include "hwrev.h" +#include "onewire.h" +#include "nxp-fb.h" + +#include <env_internal.h> /* for env_save() */ +#include <asm/mach-types.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum gpio_group { + gpio_a, gpio_b, gpio_c, gpio_d, gpio_e, +}; + +#ifdef CONFIG_PWM_NX +struct pwm_device { + int grp; + int bit; + int io_fn; +}; + +static inline void bd_pwm_config_gpio(int ch) +{ + struct pwm_device pwm_dev[] = { + [0] = { .grp = gpio_d, .bit = 1, .io_fn = 0 }, + [1] = { .grp = gpio_c, .bit = 13, .io_fn = 1 }, + [2] = { .grp = gpio_c, .bit = 14, .io_fn = 1 }, + [3] = { .grp = gpio_d, .bit = 0, .io_fn = 0 }, + }; + + int gp = pwm_dev[ch].grp; + int io = pwm_dev[ch].bit; + + /* pwm backlight OFF: HIGH, ON: LOW */ + nx_gpio_set_pad_function(gp, io, pwm_dev[ch].io_fn); + nx_gpio_set_output_value(gp, io, 1); + nx_gpio_set_output_enable(gp, io, 1); +} +#endif + +static void bd_backlight_off(void) +{ +#ifdef CONFIG_S5P4418_ONEWIRE + onewire_set_backlight(0); + +#elif defined(BACKLIGHT_CH) + bd_pwm_config_gpio(BACKLIGHT_CH); +#endif +} + +static void bd_backlight_on(void) +{ +#ifdef CONFIG_S5P4418_ONEWIRE + onewire_set_backlight(127); + +#elif defined(BACKLIGHT_CH) + /* pwm backlight ON: HIGH, ON: LOW */ + pwm_init(BACKLIGHT_CH, + BACKLIGHT_DIV, BACKLIGHT_INV); + pwm_config(BACKLIGHT_CH, + TO_DUTY_NS(BACKLIGHT_DUTY, BACKLIGHT_HZ), + TO_PERIOD_NS(BACKLIGHT_HZ)); +#endif +} + +static void bd_lcd_config_gpio(void) +{ + int i; + + for (i = 0; i < 28; i++) { + nx_gpio_set_pad_function(gpio_a, i, 1); + nx_gpio_set_drive_strength(gpio_a, i, 0); + nx_gpio_set_pull_mode(gpio_a, i, 2); + } + + nx_gpio_set_drive_strength(gpio_a, 0, 1); +} + +/* DEFAULT mmc dev for eMMC boot (dwmmc.2) */ +static int mmc_boot_dev = 0; + +int board_mmc_bootdev(void) +{ + return mmc_boot_dev; +} + +/* call from common/env_mmc.c */ +int mmc_get_env_dev(void) +{ + return mmc_boot_dev; +} + +#ifdef CONFIG_DISPLAY_BOARDINFO +int checkboard(void) +{ + printf("Board: %s\n", get_board_name()); + + return 0; +} +#endif + +int nx_display_fixup_dp(struct nx_display_dev *dp) +{ + struct nxp_lcd *lcd = bd_get_lcd(); + enum lcd_format fmt = bd_get_lcd_format(); + struct nxp_lcd_timing *timing = &lcd->timing; + struct dp_sync_info *sync = &dp->sync; + struct dp_plane_info *plane = &dp->planes[0]; + int i; + u32 clk = 800000000; + u32 div; + + sync->h_active_len = lcd->width; + sync->h_sync_width = timing->h_sw; + sync->h_back_porch = timing->h_bp; + sync->h_front_porch = timing->h_fp; + sync->h_sync_invert = !lcd->polarity.inv_hsync; + + sync->v_active_len = lcd->height; + sync->v_sync_width = timing->v_sw; + sync->v_back_porch = timing->v_bp; + sync->v_front_porch = timing->v_fp; + sync->v_sync_invert = !lcd->polarity.inv_vsync; + + /* calculates pixel clock */ + div = timing->h_sw + timing->h_bp + timing->h_fp + lcd->width; + div *= timing->v_sw + timing->v_bp + timing->v_fp + lcd->height; + div *= lcd->freq ? : 60; + clk /= div; + + dp->ctrl.clk_div_lv0 = clk; + dp->ctrl.clk_inv_lv0 = lcd->polarity.rise_vclk; + + dp->top.screen_width = lcd->width; + dp->top.screen_height = lcd->height; + + for (i = 0; i < dp->top.plane_num; i++, plane++) { + if (plane->enable) { + plane->width = lcd->width; + plane->height = lcd->height; + } + } + + /* initialize display device type */ + if (fmt == LCD_RGB) { + dp->dev_type = DP_DEVICE_RGBLCD; + + } else if (fmt == LCD_HDMI) { + struct dp_hdmi_dev *dev = (struct dp_hdmi_dev *)dp->device; + + dp->dev_type = DP_DEVICE_HDMI; + if (lcd->width == 1920 && lcd->height == 1080) + dev->preset = 1; + else + dev->preset = 0; + + } else { + struct dp_lvds_dev *dev = (struct dp_lvds_dev *)dp->device; + + dp->dev_type = DP_DEVICE_LVDS; + dev->lvds_format = (fmt & 0x3); + } + + return 0; +} + +/* -------------------------------------------------------------------------- + * initialize board status. + */ + +#define MMC_BOOT_CH0 (0) +#define MMC_BOOT_CH1 (1 << 3) +#define MMC_BOOT_CH2 (1 << 19) + +static void bd_bootdev_init(void) +{ + unsigned int rst = readl(PHY_BASEADDR_CLKPWR + SYSRSTCONFIG); + + rst &= (1 << 19) | (1 << 3); + if (rst == MMC_BOOT_CH0) { + /* mmc dev 1 for SD boot */ + mmc_boot_dev = 1; + } +} + +static void bd_onewire_init(void) +{ + unsigned char lcd; + unsigned short fw_ver; + + onewire_init(); + onewire_get_info(&lcd, &fw_ver); +} + +static void bd_lcd_init(void) +{ + struct nxp_lcd *cfg; + int id; + int ret; + + id = onewire_get_lcd_id(); + /* -1: onwire probe failed + * 0: bad + * >0: identified + */ + + ret = bd_setup_lcd_by_id(id); + if (id <= 0 || ret != id) { + printf("Panel: N/A (%d)\n", id); + bd_setup_lcd_by_name("HDMI720P60"); + + } else { + printf("Panel: %s\n", bd_get_lcd_name()); + + cfg = bd_get_lcd(); + if (cfg->gpio_init) + cfg->gpio_init(); + } +} + +static int mac_read_from_generic_eeprom(u8 *addr) +{ + return -1; +} + +static void make_ether_addr(u8 *addr) +{ + u32 hash[20]; + +#define ETHER_MAC_TAG "ethmac" + memset(hash, 0, sizeof(hash)); + memcpy(hash + 12, ETHER_MAC_TAG, sizeof(ETHER_MAC_TAG)); + + hash[4] = readl(PHY_BASEADDR_ECID + 0x00); + hash[5] = readl(PHY_BASEADDR_ECID + 0x04); + hash[6] = readl(PHY_BASEADDR_ECID + 0x08); + hash[7] = readl(PHY_BASEADDR_ECID + 0x0c); + + md5((unsigned char *)&hash[4], 64, (unsigned char *)hash); + + hash[0] ^= hash[2]; + hash[1] ^= hash[3]; + + memcpy(addr, (char *)hash, 6); + addr[0] &= 0xfe; /* clear multicast bit */ + addr[0] |= 0x02; +} + +static void set_ether_addr(void) +{ + unsigned char mac[6]; + char ethaddr[20]; + int ret; + + if (env_get("ethaddr")) + return; + + ret = mac_read_from_generic_eeprom(mac); + if (ret < 0) + make_ether_addr(mac); + + sprintf(ethaddr, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + if (!ret) + printf("MAC: [%s]\n", ethaddr); + + env_set("ethaddr", ethaddr); +} + +#ifdef CONFIG_REVISION_TAG +static void set_board_rev(void) +{ + char info[64] = {0, }; + + snprintf(info, ARRAY_SIZE(info), "%02x", get_board_rev()); + env_set("board_rev", info); +} +#endif + +static void set_dtb_name(void) +{ + char info[64] = {0, }; + + snprintf(info, ARRAY_SIZE(info), + "s5p4418-nanopi2-rev%02x.dtb", get_board_rev()); + env_set("dtb_name", info); +} + +static void bd_update_env(void) +{ + char *lcdtype = env_get("lcdtype"); + char *lcddpi = env_get("lcddpi"); + char *bootargs = env_get("bootargs"); + const char *name; + char *p = NULL; + int rootdev = board_mmc_bootdev(); + int need_save = 0; + +#define CMDLINE_LCD " lcd=" + char cmdline[CONFIG_SYS_CBSIZE]; + int n = 1; + + if (rootdev != CONFIG_ROOT_DEV && !env_get("firstboot")) { + env_set_ulong("rootdev", rootdev); + env_set("firstboot", "0"); + need_save = 1; + } + + if (lcdtype) { + /* Setup again as user specified LCD in env */ + bd_setup_lcd_by_name(lcdtype); + } + + name = bd_get_lcd_name(); + + if (bootargs) + n = strlen(bootargs); /* isn't 0 for NULL */ + else + cmdline[0] = '\0'; + + if ((n + strlen(name) + sizeof(CMDLINE_LCD)) > sizeof(cmdline)) { + printf("Error: `bootargs' is too large (%d)\n", n); + goto __exit; + } + + if (bootargs) { + p = strstr(bootargs, CMDLINE_LCD); + if (p) { + n = (p - bootargs); + p += strlen(CMDLINE_LCD); + } + strncpy(cmdline, bootargs, n); + } + + /* add `lcd=NAME,NUMdpi' */ + strncpy(cmdline + n, CMDLINE_LCD, strlen(CMDLINE_LCD)); + n += strlen(CMDLINE_LCD); + + strcpy(cmdline + n, name); + n += strlen(name); + + if (lcddpi) { + n += sprintf(cmdline + n, ",%sdpi", lcddpi); + } else { + int dpi = bd_get_lcd_density(); + + if (dpi > 0 && dpi < 600) + n += sprintf(cmdline + n, ",%ddpi", dpi); + } + + /* copy remaining of bootargs */ + if (p) { + p = strstr(p, " "); + if (p) { + strcpy(cmdline + n, p); + n += strlen(p); + } + } + + /* append `bootdev=2' */ +#define CMDLINE_BDEV " bootdev=" + if (rootdev > 0 && !strstr(cmdline, CMDLINE_BDEV)) + n += sprintf(cmdline + n, "%s2", CMDLINE_BDEV); + + /* finally, let's update uboot env & save it */ + if (bootargs && strncmp(cmdline, bootargs, sizeof(cmdline))) { + env_set("bootargs", cmdline); + need_save = 1; + } + +__exit: + if (need_save) + env_save(); +} + +/* -------------------------------------------------------------------------- + * call from u-boot + */ + +int board_early_init_f(void) +{ + return 0; +} + +int board_init(void) +{ + bd_hwrev_init(); + bd_base_rev_init(); + + bd_bootdev_init(); + bd_onewire_init(); + + bd_backlight_off(); + + bd_lcd_config_gpio(); + bd_lcd_init(); + +#ifdef CONFIG_SILENT_CONSOLE + gd->flags |= GD_FLG_SILENT; +#endif + + return 0; +} + +#ifdef CONFIG_BOARD_LATE_INIT +int board_late_init(void) +{ + bd_update_env(); + +#ifdef CONFIG_REVISION_TAG + set_board_rev(); +#endif + set_dtb_name(); + + set_ether_addr(); + +#ifdef CONFIG_SILENT_CONSOLE + gd->flags &= ~GD_FLG_SILENT; +#endif + + bd_backlight_on(); + printf("\n"); + + return 0; +} +#endif + +#ifdef CONFIG_SPLASH_SOURCE +#include <splash.h> +static struct splash_location splash_locations[] = { + { + .name = "mmc_fs", + .storage = SPLASH_STORAGE_MMC, + .flags = SPLASH_STORAGE_FS, + .devpart = __stringify(CONFIG_ROOT_DEV) ":" + __stringify(CONFIG_BOOT_PART), + }, +}; + +int splash_screen_prepare(void) +{ + int err; + char *env_cmd = env_get("load_splash"); + + debug("%s()\n", __func__); + + if (env_cmd) { + err = run_command(env_cmd, 0); + + } else { + char devpart[64] = { 0, }; + int bootpart = env_get_ulong("bootpart", 0, CONFIG_BOOT_PART); + int rootdev; + + if (env_get("firstboot")) + rootdev = env_get_ulong("rootdev", 0, CONFIG_ROOT_DEV); + else + rootdev = board_mmc_bootdev(); + + snprintf(devpart, ARRAY_SIZE(devpart), "%d:%d", rootdev, + bootpart); + splash_locations[0].devpart = devpart; + + err = splash_source_load(splash_locations, + ARRAY_SIZE(splash_locations)); + } + + if (!err) { + char addr[64]; + + sprintf(addr, "0x%lx", gd->fb_base); + env_set("fb_addr", addr); + } + + return err; +} +#endif + +/* u-boot dram initialize */ +int dram_init(void) +{ + gd->ram_size = CONFIG_SYS_SDRAM_SIZE; + return 0; +} + +/* u-boot dram board specific */ +int dram_init_banksize(void) +{ +#define SCR_USER_SIG6_READ (SCR_ALIVE_BASE + 0x0F0) + unsigned int reg_val = readl(SCR_USER_SIG6_READ); + + /* set global data memory */ + gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x00000100; + + gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE; + gd->bd->bi_dram[0].size = CONFIG_SYS_SDRAM_SIZE; + + /* Number of Row: 14 bits */ + if ((reg_val >> 28) == 14) + gd->bd->bi_dram[0].size -= 0x20000000; + + /* Number of Memory Chips */ + if ((reg_val & 0x3) > 1) { + gd->bd->bi_dram[1].start = 0x80000000; + gd->bd->bi_dram[1].size = 0x40000000; + } + return 0; +} + +#if defined(CONFIG_OF_BOARD_SETUP) +int ft_board_setup(void *blob, bd_t *bd) +{ + int nodeoff; + unsigned int rootdev; + unsigned int fb_addr; + + if (board_mmc_bootdev() > 0) { + rootdev = fdt_getprop_u32_default(blob, "/board", "sdidx", 2); + if (rootdev) { + /* find or create "/chosen" node. */ + nodeoff = fdt_find_or_add_subnode(blob, 0, "chosen"); + if (nodeoff >= 0) + fdt_setprop_u32(blob, nodeoff, "linux,rootdev", + rootdev); + } + } + + fb_addr = env_get_ulong("fb_addr", 0, 0); + if (fb_addr) { + nodeoff = fdt_path_offset(blob, "/reserved-memory"); + if (nodeoff < 0) + return nodeoff; + + nodeoff = fdt_add_subnode(blob, nodeoff, "display_reserved"); + if (nodeoff >= 0) { + fdt32_t cells[2]; + + cells[0] = cpu_to_fdt32(fb_addr); + cells[1] = cpu_to_fdt32(0x800000); + + fdt_setprop(blob, nodeoff, "reg", cells, + sizeof(cells[0]) * 2); + } + } + + return 0; +} +#endif diff --git a/board/friendlyarm/nanopi2/hwrev.c b/board/friendlyarm/nanopi2/hwrev.c new file mode 100644 index 0000000..10a41cd --- /dev/null +++ b/board/friendlyarm/nanopi2/hwrev.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd. + * (http://www.friendlyarm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +#include <config.h> +#include <common.h> +#include <i2c.h> +#include <asm/io.h> + +#include <asm/arch/nexell.h> +#include <asm/arch/nx_gpio.h> + +/* Board revision list: <PCB3 | PCB2 | PCB1> + * 0b000 - NanoPi 2 + * 0b001 - NanoPC-T2 + * 0b010 - NanoPi S2 + * 0b011 - Smart4418 + * 0b100 - NanoPi Fire 2A + * 0b111 - NanoPi M2A + * + * Extented revision: + * 0b001 - Smart4418-SDK + */ +#define __IO_GRP 2 /* GPIO_C */ +#define __IO_PCB1 26 +#define __IO_PCB2 27 +#define __IO_PCB3 25 + +static int pcb_rev = -1; +static int base_rev = 0; + +static void bd_hwrev_config_gpio(void) +{ + int gpios[3][2] = { + { __IO_PCB1, 1 }, + { __IO_PCB2, 1 }, + { __IO_PCB3, 1 }, + }; + int i; + + /* gpio input mode, pull-down */ + for (i = 0; i < 3; i++) { + nx_gpio_set_pad_function(__IO_GRP, gpios[i][0], gpios[i][1]); + nx_gpio_set_output_enable(__IO_GRP, gpios[i][0], 0); + nx_gpio_set_pull_mode(__IO_GRP, gpios[i][0], 0); + } +} + +void bd_hwrev_init(void) +{ + if (pcb_rev >= 0) + return; + + bd_hwrev_config_gpio(); + + pcb_rev = nx_gpio_get_input_value(__IO_GRP, __IO_PCB1); + pcb_rev |= nx_gpio_get_input_value(__IO_GRP, __IO_PCB2) << 1; + pcb_rev |= nx_gpio_get_input_value(__IO_GRP, __IO_PCB3) << 2; +} + +/* Get extended revision for SmartXX18 */ +void bd_base_rev_init(void) +{ + struct udevice *dev; + u8 val = 0; + + if (pcb_rev != 0x3) + return; + +#define PCA9536_I2C_BUS 2 +#define PCA9636_I2C_ADDR 0x41 + if (i2c_get_chip_for_busnum + (PCA9536_I2C_BUS, PCA9636_I2C_ADDR, 1, &dev)) + return; + + if (!dm_i2c_read(dev, 0, &val, 1)) + base_rev = (val & 0xf); +} + +/* To override __weak symbols */ +u32 get_board_rev(void) +{ + return (base_rev << 8) | pcb_rev; +} + +const char *get_board_name(void) +{ + bd_hwrev_init(); + + switch (pcb_rev) { + case 0: + return "NanoPi 2"; + case 1: + return "NanoPC-T2"; + case 2: + return "NanoPi S2"; + case 3: + return "Smart4418"; + case 4: + return "NanoPi Fire 2A"; + case 7: + return "NanoPi M2A"; + default: + return "s5p4418-X"; + } +} diff --git a/board/friendlyarm/nanopi2/hwrev.h b/board/friendlyarm/nanopi2/hwrev.h new file mode 100644 index 0000000..7bd1b67 --- /dev/null +++ b/board/friendlyarm/nanopi2/hwrev.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd. + * (http://www.friendlyarm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +#ifndef __BD_HW_REV_H__ +#define __BD_HW_REV_H__ + +extern void bd_hwrev_init(void); +extern void bd_base_rev_init(void); +extern u32 get_board_rev(void); +extern const char *get_board_name(void); + +#endif /* __BD_HW_REV_H__ */ diff --git a/board/friendlyarm/nanopi2/lcds.c b/board/friendlyarm/nanopi2/lcds.c new file mode 100644 index 0000000..3d2d66f --- /dev/null +++ b/board/friendlyarm/nanopi2/lcds.c @@ -0,0 +1,703 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * board/s5p4418/nanopi2/lcds.c + * + * Copyright (c) 2017 FriendlyARM (www.arm9.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <config.h> +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <fdt_support.h> +#include <asm/io.h> + +#include <asm/arch/nexell.h> +#include <asm/arch/display.h> +#include <asm/arch/nx_gpio.h> + +#include "nxp-fb.h" + +/* + * param @module_index for nx_gpio APIs and will be removed + * after support pinctrl + */ +#ifndef PAD_GPIO_A +#define PAD_GPIO_A 0 +#endif + +static inline void common_gpio_init(void) +{ + /* PVCLK */ + nx_gpio_set_fast_slew(PAD_GPIO_A, 0, 1); +} + +static void s70_gpio_init(void) +{ + int i; + + /* PVCLK */ + nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 1); + + /* RGB24 */ + for (i = 1; i < 25; i++) + nx_gpio_set_drive_strength(PAD_GPIO_A, i, 2); + + /* HS/VS/DE */ + for (; i < 28; i++) + nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1); +} + +static void s702_gpio_init(void) +{ + int i; + + common_gpio_init(); + + nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 2); + + for (i = 1; i < 25; i++) + nx_gpio_set_drive_strength(PAD_GPIO_A, i, 0); + + for (; i < 28; i++) + nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1); +} + +static void s430_gpio_init(void) +{ + int i; + + for (i = 0; i < 28; i++) + nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1); +} + +static void hd101_gpio_init(void) +{ + int i; + + common_gpio_init(); + + nx_gpio_set_drive_strength(PAD_GPIO_A, 0, 2); + + for (i = 1; i < 25; i++) + nx_gpio_set_drive_strength(PAD_GPIO_A, i, 1); + + nx_gpio_set_drive_strength(PAD_GPIO_A, 27, 1); +} + +static void hd700_gpio_init(void) +{ + hd101_gpio_init(); +} + +/* NXP display configs for supported LCD */ + +static struct nxp_lcd wxga_hd700 = { + .width = 800, + .height = 1280, + .p_width = 94, + .p_height = 151, + .bpp = 24, + .freq = 60, + + .timing = { + .h_fp = 20, + .h_bp = 20, + .h_sw = 24, + .v_fp = 4, + .v_fpe = 1, + .v_bp = 4, + .v_bpe = 1, + .v_sw = 8, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 0, + .inv_vsync = 0, + .inv_vden = 0, + }, + .gpio_init = hd700_gpio_init, +}; + +static struct nxp_lcd wvga_s70 = { + .width = 800, + .height = 480, + .p_width = 155, + .p_height = 93, + .bpp = 24, + .freq = 61, + + .timing = { + .h_fp = 48, + .h_bp = 36, + .h_sw = 10, + .v_fp = 22, + .v_fpe = 1, + .v_bp = 15, + .v_bpe = 1, + .v_sw = 8, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, + .gpio_init = s70_gpio_init, +}; + +static struct nxp_lcd wvga_s702 = { + .width = 800, + .height = 480, + .p_width = 155, + .p_height = 93, + .bpp = 24, + .freq = 61, + + .timing = { + .h_fp = 44, + .h_bp = 26, + .h_sw = 20, + .v_fp = 22, + .v_fpe = 1, + .v_bp = 15, + .v_bpe = 1, + .v_sw = 8, + }, + .polarity = { + .rise_vclk = 1, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, + .gpio_init = s702_gpio_init, +}; + +static struct nxp_lcd wvga_s70d = { + .width = 800, + .height = 480, + .p_width = 155, + .p_height = 93, + .bpp = 24, + .freq = 61, + + .timing = { + .h_fp = 80, + .h_bp = 78, + .h_sw = 10, + .v_fp = 22, + .v_fpe = 1, + .v_bp = 24, + .v_bpe = 1, + .v_sw = 8, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, + .gpio_init = s702_gpio_init, +}; + +static struct nxp_lcd wvga_w50 = { + .width = 800, + .height = 480, + .p_width = 108, + .p_height = 64, + .bpp = 24, + .freq = 61, + + .timing = { + .h_fp = 40, + .h_bp = 40, + .h_sw = 48, + .v_fp = 20, + .v_fpe = 1, + .v_bp = 20, + .v_bpe = 1, + .v_sw = 12, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, + .gpio_init = s70_gpio_init, +}; + +static struct nxp_lcd wvga_s430 = { + .width = 480, + .height = 800, + .p_width = 108, + .p_height = 64, + .bpp = 24, + .freq = 60, + + .timing = { + .h_fp = 64, + .h_bp = 0, + .h_sw = 16, + .v_fp = 32, + .v_fpe = 1, + .v_bp = 0, + .v_bpe = 1, + .v_sw = 16, + }, + .polarity = { + .rise_vclk = 1, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, + .gpio_init = s430_gpio_init, +}; + +static struct nxp_lcd wsvga_w101 = { + .width = 1024, + .height = 600, + .p_width = 204, + .p_height = 120, + .bpp = 24, + .freq = 60, + + .timing = { + .h_fp = 40, + .h_bp = 40, + .h_sw = 200, + .v_fp = 8, + .v_fpe = 1, + .v_bp = 8, + .v_bpe = 1, + .v_sw = 16, + }, + .polarity = { + .rise_vclk = 1, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, +}; + +static struct nxp_lcd wsvga_x710 = { + .width = 1024, + .height = 600, + .p_width = 154, + .p_height = 90, + .bpp = 24, + .freq = 61, + + .timing = { + .h_fp = 84, + .h_bp = 84, + .h_sw = 88, + .v_fp = 10, + .v_fpe = 1, + .v_bp = 10, + .v_bpe = 1, + .v_sw = 20, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, + .gpio_init = hd101_gpio_init, +}; + +static struct nxp_lcd xga_a97 = { + .width = 1024, + .height = 768, + .p_width = 200, + .p_height = 150, + .bpp = 24, + .freq = 61, + + .timing = { + .h_fp = 12, + .h_bp = 12, + .h_sw = 4, + .v_fp = 8, + .v_fpe = 1, + .v_bp = 8, + .v_bpe = 1, + .v_sw = 4, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, +}; + +static struct nxp_lcd xga_lq150 = { + .width = 1024, + .height = 768, + .p_width = 304, + .p_height = 228, + .bpp = 24, + .freq = 60, + + .timing = { + .h_fp = 12, + .h_bp = 12, + .h_sw = 40, + .v_fp = 8, + .v_fpe = 1, + .v_bp = 8, + .v_bpe = 1, + .v_sw = 40, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, +}; + +static struct nxp_lcd vga_l80 = { + .width = 640, + .height = 480, + .p_width = 160, + .p_height = 120, + .bpp = 32, + .freq = 60, + + .timing = { + .h_fp = 35, + .h_bp = 53, + .h_sw = 73, + .v_fp = 3, + .v_fpe = 1, + .v_bp = 29, + .v_bpe = 1, + .v_sw = 6, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, +}; + +static struct nxp_lcd wxga_bp101 = { + .width = 1280, + .height = 800, + .p_width = 218, + .p_height = 136, + .bpp = 24, + .freq = 60, + + .timing = { + .h_fp = 20, + .h_bp = 20, + .h_sw = 24, + .v_fp = 4, + .v_fpe = 1, + .v_bp = 4, + .v_bpe = 1, + .v_sw = 8, + }, + .polarity = { + .rise_vclk = 1, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, +}; + +static struct nxp_lcd wxga_hd101 = { + .width = 1280, + .height = 800, + .p_width = 218, + .p_height = 136, + .bpp = 24, + .freq = 60, + + .timing = { + .h_fp = 16, + .h_bp = 16, + .h_sw = 30, + .v_fp = 8, + .v_fpe = 1, + .v_bp = 8, + .v_bpe = 1, + .v_sw = 12, + }, + .polarity = { + .rise_vclk = 1, + .inv_hsync = 0, + .inv_vsync = 0, + .inv_vden = 0, + }, + .gpio_init = hd101_gpio_init, +}; + +static struct nxp_lcd hvga_h43 = { + .width = 480, + .height = 272, + .p_width = 96, + .p_height = 54, + .bpp = 32, + .freq = 65, + + .timing = { + .h_fp = 5, + .h_bp = 40, + .h_sw = 2, + .v_fp = 8, + .v_fpe = 1, + .v_bp = 8, + .v_bpe = 1, + .v_sw = 2, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, +}; + +static struct nxp_lcd hvga_p43 = { + .width = 480, + .height = 272, + .p_width = 96, + .p_height = 54, + .bpp = 32, + .freq = 65, + + .timing = { + .h_fp = 5, + .h_bp = 40, + .h_sw = 2, + .v_fp = 8, + .v_fpe = 1, + .v_bp = 9, + .v_bpe = 1, + .v_sw = 2, + }, + .polarity = { + .rise_vclk = 1, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, +}; + +static struct nxp_lcd qvga_w35 = { + .width = 320, + .height = 240, + .p_width = 70, + .p_height = 52, + .bpp = 16, + .freq = 65, + + .timing = { + .h_fp = 4, + .h_bp = 70, + .h_sw = 4, + .v_fp = 4, + .v_fpe = 1, + .v_bp = 12, + .v_bpe = 1, + .v_sw = 4, + }, + .polarity = { + .rise_vclk = 1, + .inv_hsync = 0, + .inv_vsync = 0, + .inv_vden = 0, + }, +}; + +/* HDMI */ +static struct nxp_lcd hdmi_def = { + .width = 1920, + .height = 1080, + .p_width = 480, + .p_height = 320, + .bpp = 24, + .freq = 60, + + .timing = { + .h_fp = 12, + .h_bp = 12, + .h_sw = 4, + .v_fp = 8, + .v_fpe = 1, + .v_bp = 8, + .v_bpe = 1, + .v_sw = 4, + }, + .polarity = { + .rise_vclk = 0, + .inv_hsync = 1, + .inv_vsync = 1, + .inv_vden = 0, + }, +}; + +static struct hdmi_config { + char *name; + int width; + int height; +} bd_hdmi_config[] = { + { "HDMI1080P60", 1920, 1080 }, + { "HDMI1080I60", 1920, 1080 }, + { "HDMI1080P30", 1920, 1080 }, + { "HDMI1080P50", 1920, 1080 }, + { "HDMI1080I50", 1920, 1080 }, + + { "HDMI1080P60D", 960, 536 }, + { "HDMI1080I60D", 960, 536 }, + { "HDMI1080P30D", 960, 536 }, + { "HDMI1080P50D", 960, 536 }, + { "HDMI1080I50D", 960, 536 }, + + { "HDMI720P60", 1280, 720 }, + { "HDMI720P60D", 640, 360 }, + { "HDMI720P50", 1280, 720 }, + { "HDMI720P50D", 640, 360 }, + + { "HDMI576P16X9", 720, 576 }, + { "HDMI576P16X9D", 720, 576 }, + { "HDMI576P4X3", 720, 576 }, + { "HDMI576P4X3D", 720, 576 }, + + { "HDMI480P16X9", 720, 480 }, + { "HDMI480P16X9D", 720, 480 }, + { "HDMI480P4X3", 720, 480 }, + { "HDMI480P4X3D", 720, 480 }, +}; + +/* Try to guess LCD panel by kernel command line, or + * using *HD101* as default + */ +static struct { + int id; + char *name; + struct nxp_lcd *lcd; + int dpi; + int ctp; + enum lcd_format fmt; +} bd_lcd_config[] = { + { 25, "HD101", &wxga_hd101, 0, 1, LCD_RGB }, + { 32, "HD101B", &wxga_hd101, 0, 1, LCD_RGB }, + { 18, "HD700", &wxga_hd700, 213, 1, LCD_RGB }, + { 30, "HD702", &wxga_hd700, 213, 1, LCD_RGB }, + { 33, "H70", &wxga_hd700, 213, 0, LCD_VESA }, + { 3, "S70", &wvga_s70, 128, 1, LCD_RGB }, + { 36, "S701", &wvga_s70, 128, 1, LCD_RGB }, + { 24, "S702", &wvga_s702, 128, 3, LCD_RGB }, + { 26, "S70D", &wvga_s70d, 128, 0, LCD_RGB }, + { 14, "H43", &hvga_h43, 0, 0, LCD_RGB }, + { 19, "P43", &hvga_p43, 0, 0, LCD_RGB }, + { 8, "W35", &qvga_w35, 0, 0, LCD_RGB }, + { 28, "X710", &wsvga_x710, 0, 1, LCD_RGB }, + { 31, "S430", &wvga_s430, 180, 1, LCD_RGB }, + { 4, "W50", &wvga_w50, 0, 0, LCD_RGB }, + + /* TODO: Testing */ + { 15, "W101", &wsvga_w101, 0, 1, LCD_RGB }, + { 5, "L80", &vga_l80, 0, 1, LCD_RGB }, + { -1, "A97", &xga_a97, 0, 0, LCD_RGB }, + { -1, "LQ150", &xga_lq150, 0, 1, LCD_RGB }, + { -1, "BP101", &wxga_bp101, 0, 1, LCD_RGB }, + /* Pls keep it at last */ + { 128, "HDMI", &hdmi_def, 0, 0, LCD_HDMI }, +}; + +static int lcd_idx = 0; + +int bd_setup_lcd_by_id(int id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bd_lcd_config); i++) { + if (bd_lcd_config[i].id == id) { + lcd_idx = i; + break; + } + } + + if (i >= ARRAY_SIZE(bd_lcd_config)) { + /* NOT found */ + return -19; + } + + return bd_lcd_config[i].id; +} + +int bd_setup_lcd_by_name(char *str) +{ + char *delim; + int i; + + delim = strchr(str, ','); + if (delim) + *delim++ = '\0'; + + if (!strncasecmp("HDMI", str, 4)) { + struct hdmi_config *cfg = &bd_hdmi_config[0]; + struct nxp_lcd *lcd; + + lcd_idx = ARRAY_SIZE(bd_lcd_config) - 1; + lcd = bd_lcd_config[lcd_idx].lcd; + + for (i = 0; i < ARRAY_SIZE(bd_hdmi_config); i++, cfg++) { + if (!strcasecmp(cfg->name, str)) { + lcd->width = cfg->width; + lcd->height = cfg->height; + bd_lcd_config[lcd_idx].name = cfg->name; + goto __ret; + } + } + } + + for (i = 0; i < ARRAY_SIZE(bd_lcd_config); i++) { + if (!strcasecmp(bd_lcd_config[i].name, str)) { + lcd_idx = i; + break; + } + } + +__ret: + return 0; +} + +struct nxp_lcd *bd_get_lcd(void) +{ + return bd_lcd_config[lcd_idx].lcd; +} + +const char *bd_get_lcd_name(void) +{ + return bd_lcd_config[lcd_idx].name; +} + +enum lcd_format bd_get_lcd_format(void) +{ + return bd_lcd_config[lcd_idx].fmt; +} + +int bd_get_lcd_density(void) +{ + return bd_lcd_config[lcd_idx].dpi; +} + +#if CONFIG_IS_ENABLED(OF_CONTROL) +int bd_fixup_lcd_fdt(void *blob, struct nxp_lcd *lcd) +{ + return 0; +} +#endif diff --git a/board/friendlyarm/nanopi2/nxp-fb.h b/board/friendlyarm/nanopi2/nxp-fb.h new file mode 100644 index 0000000..9470fa8 --- /dev/null +++ b/board/friendlyarm/nanopi2/nxp-fb.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * board/s5p4418/nanopi2/nxp-fb.h + * + * Copyright (c) 2017 FriendlyARM (www.arm9.net) + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Header file for NXP Display Driver driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __MACH_NXP_FB_H__ +#define __MACH_NXP_FB_H__ + +/* + * struct nxp_lcd_polarity + * @rise_vclk: if 1, video data is fetched at rising edge + * @inv_hsync: if HSYNC polarity is inversed + * @inv_vsync: if VSYNC polarity is inversed + * @inv_vden: if VDEN polarity is inversed + */ +struct nxp_lcd_polarity { + int rise_vclk; + int inv_hsync; + int inv_vsync; + int inv_vden; +}; + +/* + * struct nxp_lcd_timing + * @h_fp: horizontal front porch + * @h_bp: horizontal back porch + * @h_sw: horizontal sync width + * @v_fp: vertical front porch + * @v_fpe: vertical front porch for even field + * @v_bp: vertical back porch + * @v_bpe: vertical back porch for even field + */ +struct nxp_lcd_timing { + int h_fp; + int h_bp; + int h_sw; + int v_fp; + int v_fpe; + int v_bp; + int v_bpe; + int v_sw; +}; + +/* + * struct nxp_lcd + * @width: horizontal resolution + * @height: vertical resolution + * @p_width: width of lcd in mm + * @p_height: height of lcd in mm + * @bpp: bits per pixel + * @freq: vframe frequency + * @timing: timing values + * @polarity: polarity settings + * @gpio_init: pointer to GPIO init function + * + */ +struct nxp_lcd { + int width; + int height; + int p_width; + int p_height; + int bpp; + int freq; + struct nxp_lcd_timing timing; + struct nxp_lcd_polarity polarity; + void (*gpio_init)(void); +}; + +/** + * Public interfaces + */ +enum lcd_format { + LCD_VESA = 0, + LCD_JEIDA = 1, + LCD_LOC = 2, + + LCD_RGB = 4, + LCD_HDMI = 5, +}; + +extern int bd_setup_lcd_by_id(int id); +extern int bd_setup_lcd_by_name(char *name); +extern struct nxp_lcd *bd_get_lcd(void); +extern const char *bd_get_lcd_name(void); +extern int bd_get_lcd_density(void); +extern enum lcd_format bd_get_lcd_format(void); +extern int bd_fixup_lcd_fdt(void *blob, struct nxp_lcd *cfg); + +#endif /* __MACH_NXP_FB_H__ */ diff --git a/board/friendlyarm/nanopi2/onewire.c b/board/friendlyarm/nanopi2/onewire.c new file mode 100644 index 0000000..85314f4 --- /dev/null +++ b/board/friendlyarm/nanopi2/onewire.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd. + * (http://www.friendlyarm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +#include <config.h> +#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include <i2c.h> +#ifdef CONFIG_PWM_NX +#include <pwm.h> +#endif + +#include <irq_func.h> + +#include <asm/arch/nexell.h> +#include <asm/arch/nx_gpio.h> + +#ifndef NSEC_PER_SEC +#define NSEC_PER_SEC 1000000000L +#endif + +#define SAMPLE_BPS 9600 +#define SAMPLE_IN_US 101 /* (1000000 / BPS) */ + +#define REQ_INFO 0x60U +#define REQ_BL 0x80U + +#define BUS_I2C 0x18 +#define ONEWIRE_I2C_BUS 2 +#define ONEWIRE_I2C_ADDR 0x2f + +static int bus_type = -1; +static int lcd_id = -1; +static unsigned short lcd_fwrev = 0; +static int current_brightness = -1; +#ifdef CONFIG_DM_I2C +static struct udevice *i2c_dev; +#endif + +/* debug */ +#if (0) +#define DBGOUT(msg...) do { printf("onewire: " msg); } while (0) +#else +#define DBGOUT(msg...) do {} while (0) +#endif + +/* based on web page from http://lfh1986.blogspot.com */ +static unsigned char crc8_ow(unsigned int v, unsigned int len) +{ + unsigned char crc = 0xACU; + + while (len--) { + if ((crc & 0x80U) != 0) { + crc <<= 1; + crc ^= 0x7U; + } else { + crc <<= 1; + } + if ((v & (1U << 31)) != 0) + crc ^= 0x7U; + v <<= 1; + } + return crc; +} + +/* GPIO helpers */ +#define __IO_GRP 2 /* GPIOC15 */ +#define __IO_IDX 15 + +static inline void set_pin_as_input(void) +{ + nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 0); +} + +static inline void set_pin_as_output(void) +{ + nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 1); +} + +static inline void set_pin_value(int v) +{ + nx_gpio_set_output_value(__IO_GRP, __IO_IDX, !!v); +} + +static inline int get_pin_value(void) +{ + return nx_gpio_get_input_value(__IO_GRP, __IO_IDX); +} + +/* Timer helpers */ +#define PWM_CH 3 +#define PWM_TCON (PHY_BASEADDR_PWM + 0x08) +#define PWM_TCON_START (1 << 16) +#define PWM_TINT_CSTAT (PHY_BASEADDR_PWM + 0x44) + +static int onewire_init_timer(void) +{ + int period_ns = NSEC_PER_SEC / SAMPLE_BPS; + + /* range: 1080~1970 */ + period_ns -= 1525; + + return pwm_config(PWM_CH, period_ns >> 1, period_ns); +} + +static void wait_one_tick(void) +{ + unsigned int tcon; + + tcon = readl(PWM_TCON); + tcon |= PWM_TCON_START; + writel(tcon, PWM_TCON); + + while (1) { + if (readl(PWM_TINT_CSTAT) & (1 << (5 + PWM_CH))) + break; + } + + writel((1 << (5 + PWM_CH)), PWM_TINT_CSTAT); + + tcon &= ~PWM_TCON_START; + writel(tcon, PWM_TCON); +} + +/* Session handler */ +static int onewire_session(unsigned char req, unsigned char res[]) +{ + unsigned int Req; + unsigned int *Res; + int ints = disable_interrupts(); + int i; + int ret; + + Req = (req << 24) | (crc8_ow(req << 24, 8) << 16); + Res = (unsigned int *)res; + + set_pin_value(1); + set_pin_as_output(); + for (i = 0; i < 60; i++) + wait_one_tick(); + + set_pin_value(0); + for (i = 0; i < 2; i++) + wait_one_tick(); + + for (i = 0; i < 16; i++) { + int v = !!(Req & (1U << 31)); + + Req <<= 1; + set_pin_value(v); + wait_one_tick(); + } + + wait_one_tick(); + set_pin_as_input(); + wait_one_tick(); + for (i = 0; i < 32; i++) { + (*Res) <<= 1; + (*Res) |= get_pin_value(); + wait_one_tick(); + } + set_pin_value(1); + set_pin_as_output(); + + if (ints) + enable_interrupts(); + + ret = crc8_ow(*Res, 24) == res[0]; + DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %d\n", + req, res[3], res[2], res[1], res[0], ret); + + return ret; +} + +static int onewire_i2c_do_request(unsigned char req, unsigned char *buf) +{ + unsigned char tx[4]; + int ret; + + tx[0] = req; + tx[1] = crc8_ow(req << 24, 8); + +#ifdef CONFIG_DM_I2C + if (dm_i2c_write(i2c_dev, 0, tx, 2)) + return -EIO; + + if (!buf) + return 0; + + if (dm_i2c_read(i2c_dev, 0, buf, 4)) + return -EIO; +#else + if (i2c_write(ONEWIRE_I2C_ADDR, 0, 0, tx, 2)) + return -EIO; + + if (!buf) /* NO READ */ + return 0; + + if (i2c_read(ONEWIRE_I2C_ADDR, 0, 0, buf, 4)) + return -EIO; +#endif + + ret = crc8_ow((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8), 24); + DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %02x\n", + req, buf[0], buf[1], buf[2], buf[3], ret); + + return (ret == buf[3]) ? 0 : -EIO; +} + +static void onewire_i2c_init(void) +{ + unsigned char buf[4]; + int ret; + +#ifdef CONFIG_DM_I2C + ret = i2c_get_chip_for_busnum(ONEWIRE_I2C_BUS, + ONEWIRE_I2C_ADDR, 0, &i2c_dev); +#else + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + i2c_set_bus_num(ONEWIRE_I2C_BUS); + + ret = i2c_probe(ONEWIRE_I2C_ADDR); +#endif + if (ret) + return; + + ret = onewire_i2c_do_request(REQ_INFO, buf); + if (!ret) { + lcd_id = buf[0]; + lcd_fwrev = buf[1] * 0x100 + buf[2]; + bus_type = BUS_I2C; + } +} + +void onewire_init(void) +{ + /* GPIO, Pull-off */ + nx_gpio_set_pad_function(__IO_GRP, __IO_IDX, 1); + nx_gpio_set_pull_mode(__IO_GRP, __IO_IDX, 2); + + onewire_init_timer(); + onewire_i2c_init(); +} + +int onewire_get_info(unsigned char *lcd, unsigned short *fw_ver) +{ + unsigned char res[4]; + int i; + + if (bus_type == BUS_I2C && lcd_id > 0) { + *lcd = lcd_id; + *fw_ver = lcd_fwrev; + return 0; + } + + for (i = 0; i < 3; i++) { + if (onewire_session(REQ_INFO, res)) { + *lcd = res[3]; + *fw_ver = res[2] * 0x100 + res[1]; + lcd_id = *lcd; + DBGOUT("lcd = %d, fw_ver = %x\n", *lcd, *fw_ver); + return 0; + } + } + + /* LCD unknown or not connected */ + *lcd = 0; + *fw_ver = -1; + + return -1; +} + +int onewire_get_lcd_id(void) +{ + return lcd_id; +} + +int onewire_set_backlight(int brightness) +{ + unsigned char res[4]; + int i; + + if (brightness == current_brightness) + return 0; + + if (brightness > 127) + brightness = 127; + else if (brightness < 0) + brightness = 0; + + if (bus_type == BUS_I2C) { + onewire_i2c_do_request((REQ_BL | brightness), NULL); + current_brightness = brightness; + return 0; + } + + for (i = 0; i < 3; i++) { + if (onewire_session((REQ_BL | brightness), res)) { + current_brightness = brightness; + return 0; + } + } + + return -1; +} diff --git a/board/friendlyarm/nanopi2/onewire.h b/board/friendlyarm/nanopi2/onewire.h new file mode 100644 index 0000000..23a93c0 --- /dev/null +++ b/board/friendlyarm/nanopi2/onewire.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd. + * (http://www.friendlyarm.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +#ifndef __ONE_WIRE_H__ +#define __ONE_WIRE_H__ + +extern void onewire_init(void); +extern int onewire_get_info(unsigned char *lcd, unsigned short *fw_ver); +extern int onewire_get_lcd_id(void); +extern int onewire_set_backlight(int brightness); + +#endif /* __ONE_WIRE_H__ */

On Mon, Feb 03, 2020 at 09:46:39PM +0100, Stefan Bosch wrote:
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- SPL not supported yet --> no spl-dir in arch/arm/cpu/armv7/s5p4418/. Appropriate line in Makefile removed.
- cpu.c: '#include <cpu_func.h>' added.
- arch/arm/cpu/armv7/s5p4418/u-boot.lds removed, is not required anylonger.
- s5p4418.dtsi: '#include "../../../include/generated/autoconf.h"' removed, is not necessary, error at out-of-tree building. '#ifdef CONFIG_CPU_NXP4330'-blocks (2x) removed. Some minor changes regarding mmc. 'u-boot,dm-pre-reloc' added to dp0 because of added DM_VIDEO support.
- board/s5p4418/ renamed to board/friendlyarm/
- All s5p4418-boards except nanopi2 removed because there is no possibility to test the other boards.
- Kconfig: Changes to have a structure like mach-bcm283x (RaspberryPi), e.g. "config ..." entries moved from/to other Kconfig.
- "CONFIG_" removed from several s5p4418/nanopi2 specific defines because the appropriate values do not need to be configurable.
- nanopi2/board.c: All getenv(), getenv_ulong(), setenv() and saveenv() renamed to env_get(), env_get_ulong(), env_set() and env_save(), respectively. MACH_TYPE_S5P4418 is not defined anymore, therefore appropriate code removed (not necessary for DT-kernels).
- nanopi2/onewire.c: All crc8() renamed to crc8_ow() because crc8() is already defined in lib/crc8.c (with different parameters).
Signed-off-by: Stefan Bosch stefan_b@posteo.net
arch/arm/cpu/armv7/Makefile | 1 + arch/arm/cpu/armv7/s5p4418/Makefile | 6 + arch/arm/cpu/armv7/s5p4418/cpu.c | 120 ++++++ arch/arm/dts/Makefile | 3 + arch/arm/dts/s5p4418-nanopi2.dts | 109 ++++++ arch/arm/dts/s5p4418-pinctrl.dtsi | 66 ++++ arch/arm/dts/s5p4418.dtsi | 157 ++++++++
Please do the DTS files in their own commit and note what kernel release they're from. Thanks!

Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01: - Configuration changed, mainly several "CONFIG_..." moved from s5p4418_nanopi2.h to s5p4418_nanopi2_defconfig. - s5p4418_nanopi2.h: "CONFIG_" removed from several s5p4418/nanopi2 specific defines because the appropriate values do not need to be configurable.
Signed-off-by: Stefan Bosch stefan_b@posteo.net ---
MAINTAINERS | 19 +++ configs/s5p4418_nanopi2_defconfig | 177 +++++++++++++++++++++++++ doc/README.s5p4418 | 63 +++++++++ include/configs/s5p4418_nanopi2.h | 270 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 529 insertions(+) create mode 100644 configs/s5p4418_nanopi2_defconfig create mode 100644 doc/README.s5p4418 create mode 100644 include/configs/s5p4418_nanopi2.h
diff --git a/MAINTAINERS b/MAINTAINERS index 7d2729d..9e5de66 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -258,6 +258,25 @@ F: arch/arm/mach-at91/ F: board/atmel/ F: drivers/misc/microchip_flexcom.c
+ARM NEXELL S5P4418 +M: Stefan Bosch stefan_b@posteo.net +S: Maintained +F: arch/arm/cpu/armv7/s5p4418/ +F: arch/arm/dts/s5p4418* +F: arch/arm/mach-nexell/ +F: board/friendlyarm/ +F: configs/s5p4418_nanopi2_defconfig +F: doc/README.s5p4418 +F: drivers/gpio/nx_gpio.c +F: drivers/i2c/nx_i2c.c +F: drivers/mmc/nexell_dw_mmc_dm.c +F: drivers/pwm/pwm-nexell* +F: drivers/video/nexell/ +F: drivers/pwm/pwm-nexell* +F: drivers/video/nexell/ +F: drivers/video/nexell_display.c +F: include/configs/s5p4418_nanopi2.h + ARM OWL M: Manivannan Sadhasivam manivannan.sadhasivam@linaro.org S: Maintained diff --git a/configs/s5p4418_nanopi2_defconfig b/configs/s5p4418_nanopi2_defconfig new file mode 100644 index 0000000..921470a --- /dev/null +++ b/configs/s5p4418_nanopi2_defconfig @@ -0,0 +1,177 @@ +CONFIG_ARM=y +CONFIG_ARCH_NEXELL=y +CONFIG_ARCH_S5P4418=y +CONFIG_TARGET_NANOPI2=y +CONFIG_DM_I2C=y +CONFIG_DM_GPIO=y +CONFIG_DEFAULT_DEVICE_TREE="s5p4418-nanopi2" +CONFIG_FIT=y + +# Must be set to 'n' (apparently "CONFIG_EFI_LOADER=y" per default) +# otherwise boot errors "efi_runtime_relocate: Unknown relocation type 0" +CONFIG_EFI_LOADER=n + +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_FLASH is not set +CONFIG_CMD_I2C=y +CONFIG_CMD_USB=y +# CONFIG_CMD_FPGA is not set +CONFIG_CMD_GPIO=y +# CONFIG_CMD_SETEXPR is not set + +# CONFIG_CMD_NET is not set + +# Default is CONFIG_NET=y, in this case: +# Loading Environment from MMC... ## Warning: Unknown environment variable type 'm' +# OK +# CONFIG_CMD_NET=y must be set to avoid this Warning. But then: +# Net: Net Initialization Skipped +# No ethernet found. +# If CONFIG_NET=n is set additionally warning at "make s5p4418_nanopi2_defconfig": +# arch/../configs/s5p4418_nanopi2_defconfig:24:warning: override: reassigning to symbol CMD_NET +# +# --> CONFIG_NET=n set only +CONFIG_NET=n + +# CONFIG_CMD_NFS is not set +CONFIG_CMD_FDISK=y +CONFIG_CMD_EXT4_IMG_WRITE=y +CONFIG_CMD_SD_RECOVERY=y +CONFIG_CMD_PMIC=y +CONFIG_CMD_REGULATOR=y + +CONFIG_DM_I2C_GPIO=y +CONFIG_SYS_I2C_NEXELL=y +CONFIG_DM_PMIC=y +CONFIG_DM_PMIC_AXP228=y +CONFIG_DM_REGULATOR=y +CONFIG_DM_REGULATOR_AXP228=y +CONFIG_DM_PWM=n + +CONFIG_USB=y +CONFIG_DM_USB=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_STORAGE=y + +CONFIG_DISPLAY=y +CONFIG_DM_VIDEO=y +CONFIG_SYS_CONSOLE_BG_COL=0xff +CONFIG_SYS_CONSOLE_FG_COL=0x00 +CONFIG_VIDEO_NX=y +CONFIG_VIDEO_NX_RGB=y +CONFIG_VIDEO_NX_LVDS=y + +CONFIG_VIDEO_NX_HDMI=y + +CONFIG_REGEX=y +CONFIG_ERRNO_STR=y + +CONFIG_SYS_TEXT_BASE=0x74C00000 +CONFIG_SYS_RESERVE_MEM_SIZE=0x00400000 +CONFIG_NR_DRAM_BANKS=1 + +## System initialize options (board_init_f) +# board_init_f->init_sequence, call board_early_init_f +CONFIG_BOARD_LATE_INIT=y +# board_init_f->init_sequence, call print_cpuinfo +CONFIG_DISPLAY_CPUINFO=y +# board_init_f->init_sequence, call show_board_info +CONFIG_DISPLAY_BOARDINFO=y +# board_init_f, CONFIG_SYS_ICACHE_OFF +CONFIG_SYS_DCACHE_OFF=y +# board_init_r, call arch_misc_init +CONFIG_ARCH_MISC_INIT=y + +CONFIG_BOOTDELAY=1 +CONFIG_ZERO_BOOTDELAY_CHECK=y + +## U-Boot Environments +## refer to common/env_common.c + +# CONFIG_ENV_IS_IN_MMC must be set here and not in s5p4418_nanopi2.h +# otherwise CONFIG_ENV_IS_NOWHERE is set by env/Kconfig and environment +# (bootargs) are not loaded +CONFIG_ENV_IS_IN_MMC=y +CONFIG_ENV_OFFSET=0x2E0200 +CONFIG_ENV_SIZE=0x4000 +CONFIG_CMD_SAVEENV=y + +## Etc Command definition +# image info +CONFIG_CMD_IMI=y +# add command line history +CONFIG_CMDLINE_EDITING=y +CONFIG_INITRD_TAG=y +CONFIG_SUPPORT_RAW_INITRD=y +CONFIG_REVISION_TAG=y +CONFIG_CMD_BOOTZ=y + +## serial console configuration +CONFIG_CONS_INDEX=0 +CONFIG_BAUDRATE=115200 + +## SD/MMC +CONFIG_BOUNCE_BUFFER=y +CONFIG_GENERIC_MMC=y +CONFIG_MMC=y +CONFIG_MMC_DW=y +CONFIG_NEXELL_DWMMC=y +CONFIG_CMD_MMC=y +CONFIG_DM_MMC=y + +CONFIG_DOS_PARTITION=y +CONFIG_CMD_FAT=y +CONFIG_FS_FAT=y +CONFIG_FAT_WRITE=y + +CONFIG_CMD_EXT4=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_FS_EXT4=y +CONFIG_EXT4_WRITE=y + +## Fastboot and USB OTG +CONFIG_USB_FUNCTION_FASTBOOT=y +CONFIG_CMD_FASTBOOT=y +CONFIG_FASTBOOT_FLASH=y +# Has been "=CONFIG_SYS_MMC_DEV" in s5p4418_nanopi2.h of U-Boot 2016 +CONFIG_FASTBOOT_FLASH_MMC_DEV=2 +CONFIG_FASTBOOT_BUF_ADDR=0x7B000000 +# Has been "=(CONFIG_SYS_MEM_SIZE - 0x2E000000)" in s5p4418_nanopi2.h of +# U-Boot 2016 +CONFIG_FASTBOOT_BUF_SIZE=0x12000000 + +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_DUALSPEED=y +CONFIG_USB_GADGET_VBUS_DRAW=0 +CONFIG_USB_GADGET_DWC2_OTG=y +CONFIG_USB_GADGET_NX_UDC_OTG_PHY=y +CONFIG_USB_GADGET_DOWNLOAD=y +CONFIG_SYS_CACHELINE_SIZE=64 +# google +CONFIG_G_DNL_VENDOR_NUM=0x18d1 +# nexus one +CONFIG_G_DNL_PRODUCT_NUM=0x0002 +CONFIG_G_DNL_MANUFACTURER="Nexell Corporation" + +## OF_CONTROL +CONFIG_FIT_BEST_MATCH=y +CONFIG_OF_LIBFDT=y +CONFIG_OF_BOARD_SETUP=y + +## BOOTCOMMAND +CONFIG_ROOT_DEV=1 +CONFIG_BOOT_PART=1 +CONFIG_ROOT_PART=2 + +## Misc. +CONFIG_S5P4418_ONEWIRE=y +CONFIG_PWM_NX=y + +# necessary for if-cmd +CONFIG_HUSH_PARSER=y + +# set to 'n' to save memory +CONFIG_SYS_LONGHELP=y + +# uncomment for debugging (trace) of MMC-CMDs +#CONFIG_MMC_TRACE=y diff --git a/doc/README.s5p4418 b/doc/README.s5p4418 new file mode 100644 index 0000000..ac724d0 --- /dev/null +++ b/doc/README.s5p4418 @@ -0,0 +1,63 @@ + +Summary +======= + +This README is about U-Boot support for SAMSUNG's/NEXELL's ARM Cortex-A9 based +S5P4418 SoC. It is based on FriendlyARM's U-Boot v2016.01 for the NanoPi2 +(and other) boards [1]. + +Currently the following boards are supported: + +* FriendlyArm NanoPi2 [2] +* FriendlyArm NanoPC-T2 [3] + + +Build +===== + +* NanoPi2 and NanoPC-T2 + +make s5p4418_nanopi2_defconfig +make + + +Installation +============ + +- Download Official-ROMs-SDCard-20190718.7z from [4] (images files for android, + friendlyCore and LUbuntu) +- Use s5p4418-sd-lubuntu-desktop-xenial-4.4-armhf-20190718.img to make a SD-card +- Use dd in the directory where U-Boot has been built to update U-Boot: + (replace <SD-card> with the device used for the SD-card, e.g. sdc) + sudo dd seek=3841 if=u-boot.bin of=/dev/<SD-card> +- Boot the board from this SD-card + +The source code for (the used?) LUbuntu 16.04 can be found at [5]. + + +Links +===== + +[1] FriendlyArm U-boot v2016.01: + +https://github.com/friendlyarm/u-boot/tree/nanopi2-v2016.01 + + +[2] NanoPi2: + +http://wiki.friendlyarm.com/wiki/index.php/NanoPi_2 + + +[3] NanoPC-T2: + +http://wiki.friendlyarm.com/wiki/index.php/NanoPC-T2 + + +[4] FriendlyArm image files for NanoPi2: + +http://download.friendlyarm.com//NanoPi2 + + +[5] FriendlyArm LUbuntu 16.04 Source Code for NanoPi2: + +https://github.com/friendlyarm/linux/tree/nanopi2-v4.4.y diff --git a/include/configs/s5p4418_nanopi2.h b/include/configs/s5p4418_nanopi2.h new file mode 100644 index 0000000..64a3f99 --- /dev/null +++ b/include/configs/s5p4418_nanopi2.h @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * + * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd. + * (http://www.friendlyarm.com) + * + * (C) Copyright 2016 Nexell + * Hyejung Kwon cjscld15@nexell.co.kr + * + * Copyright (C) 2019 Stefan Bosch stefan_b@posteo.net + */ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#include <linux/sizes.h> +#include <asm/arch/nexell.h> + +/*----------------------------------------------------------------------- + * System memory Configuration + */ +#define CONFIG_SYS_INIT_SP_ADDR CONFIG_SYS_TEXT_BASE +#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE +#define CONFIG_SYS_MEM_SIZE 0x40000000 +#define CONFIG_SYS_SDRAM_BASE 0x71000000 + +/* + * "(CONFIG_SYS_MEM_SIZE - CONFIG_SYS_RESERVE_MEM_SIZE)" has been used in + * u-boot nanopi2-v2016.01. + * This is not working anymore because boot_fdt_add_mem_rsv_regions() in + * common/image-fdt.c has been extended: + * Also reserved-memory sections are marked as unusable. + * + * In friendlyArm Ubuntu 16.04 source arch/arm/boot/dts/s5p4418.dtsi: + * reserved-memory { + * #address-cells = <1>; + * #size-cells = <1>; + * ranges; + * + * secure_memory@b0000000 { + * reg = <0xB0000000 0x1000000>; + * nop-map; + * }; + * }; + * + * arch_lmb_reserve() of arch/arm/lib/bootm.c: + * "Allocate space for command line and board info - ... below the current + * stack pointer." + * --> Memory allocated would overlap with "secure_memory@b0000000" + * --> lmb_add_region(rgn, base==0xb0000000, size==0x1000000) fails, + * boot output: + * ... + * Kernel image @ 0x71080000 [ 0x000000 - 0x60e628 ] + * ## Flattened Device Tree blob at 7a000000 + * Booting using the fdt blob at 0x7a000000 + * ERROR: reserving fdt memory region failed (addr=b0000000 size=1000000) + * Using Device Tree in place at 7a000000, end 7a00fbf0 + * + * Starting kernel ... + * ... + */ +#define CONFIG_SYS_SDRAM_SIZE (0xb0000000 - CONFIG_SYS_SDRAM_BASE) + +#define CONFIG_SYS_MALLOC_LEN (32 * 1024 * 1024) + +#define BMP_LOAD_ADDR 0x78000000 + +/* kernel load address */ +#define CONFIG_SYS_LOAD_ADDR 0x71080000 +#define INITRD_START 0x79000000 +#define KERNEL_DTB_ADDR 0x7A000000 + +/* memtest works on */ +#define CONFIG_SYS_MEMTEST_START CONFIG_SYS_SDRAM_BASE +#define CONFIG_SYS_MEMTEST_END ((ulong)CONFIG_SYS_SDRAM_BASE + \ + (ulong)CONFIG_SYS_SDRAM_SIZE) + +/*----------------------------------------------------------------------- + * High Level System Configuration + */ +/* Not used: not need IRQ/FIQ stuff */ +#undef CONFIG_USE_IRQ +/* decrementer freq: 1ms ticks */ +#define CONFIG_SYS_HZ 1000 + +/*----------------------------------------------------------------------- + * System initialize options (board_init_f) + */ +/* board_init_f->init_sequence, call arch_cpu_init */ +#define CONFIG_ARCH_CPU_INIT + +/*----------------------------------------------------------------------- + * U-Boot default cmd + */ +#define CONFIG_CMD_MEMTEST + +/*----------------------------------------------------------------------- + * Miscellaneous configurable options + */ +#ifdef CONFIG_SYS_PROMPT +#undef CONFIG_SYS_PROMPT +/* Monitor Command Prompt */ +#define CONFIG_SYS_PROMPT "nanopi2# " +#endif + +/* Console I/O Buffer Size */ +#define CONFIG_SYS_CBSIZE 1024 +/* Print Buffer Size */ +#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ + sizeof(CONFIG_SYS_PROMPT) + 16) +/* max number of command args */ +#define CONFIG_SYS_MAXARGS 16 +/* Boot Argument Buffer Size */ +#define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE + +/*----------------------------------------------------------------------- + * allow to overwrite serial and ethaddr + */ +#define CONFIG_ENV_OVERWRITE + +#ifdef CONFIG_HUSH_PARSER +#define CONFIG_SYS_PROMPT_HUSH_PS2 "> " +#endif + +/*----------------------------------------------------------------------- + * Etc Command definition + */ +#define CONFIG_CMDLINE_TAG /* use bootargs commandline */ + +#undef CONFIG_BOOTM_NETBSD +#undef CONFIG_BOOTM_RTEMS + +/*----------------------------------------------------------------------- + * serial console configuration + */ +#define CONFIG_PL011_CLOCK 50000000 +#define CONFIG_PL01x_PORTS {(void *)PHY_BASEADDR_UART0, \ + (void *)PHY_BASEADDR_UART1, \ + (void *)PHY_BASEADDR_UART2, \ + (void *)PHY_BASEADDR_UART3} +#define CONFIG_SYS_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 } + +/*----------------------------------------------------------------------- + * PLL + */ +#define CONFIG_SYS_PLLFIN 24000000UL + +/*----------------------------------------------------------------------- + * Timer + */ +#define CONFIG_TIMER_SYS_TICK_CH 0 + +/*----------------------------------------------------------------------- + * BACKLIGHT + */ +#ifndef CONFIG_S5P4418_ONEWIRE +/* fallback to pwm */ +#define BACKLIGHT_CH 0 +#define BACKLIGHT_DIV 0 +#define BACKLIGHT_INV 0 +#define BACKLIGHT_DUTY 50 +#define BACKLIGHT_HZ 1000 +#endif + +/*----------------------------------------------------------------------- + * SD/MMC + */ +#if defined(CONFIG_MMC) +/* eMMC = 0, SD-card = 2 */ +#define CONFIG_SYS_MMC_DEV 2 +#define CONFIG_SYS_MMC_ENV_DEV CONFIG_SYS_MMC_DEV + +#endif + +/*----------------------------------------------------------------------- + * Default environment organization + */ +#if !defined(CONFIG_ENV_IS_IN_MMC) && !defined(CONFIG_ENV_IS_IN_NAND) && \ + !defined(CONFIG_ENV_IS_IN_FLASH) && !defined(CONFIG_ENV_IS_IN_EEPROM) + /* default: CONFIG_ENV_IS_NOWHERE */ + #define CONFIG_ENV_IS_NOWHERE + #define CONFIG_ENV_OFFSET 1024 + #define CONFIG_ENV_SIZE (4 * 1024) /* env size */ + /* imls - list all images found in flash, default enable so disable */ + #undef CONFIG_CMD_IMLS +#endif + +/*----------------------------------------------------------------------- + * VIDEO + */ + +#define CONFIG_VIDEO_LOGO + +#define CONFIG_SPLASH_SCREEN + +#ifdef CONFIG_VIDEO_LOGO +#define CONFIG_CMD_BMP + +#ifdef CONFIG_DM_VIDEO +#define CONFIG_BMP_24BPP +#endif + +#ifdef CONFIG_SPLASH_SCREEN +#define CONFIG_SPLASH_SOURCE 1 +#define CONFIG_SPLASH_SCREEN_ALIGN 1 +#define SPLASH_FILE logo.bmp +#endif +#endif + +/*----------------------------------------------------------------------- + * ENV + */ +#define BLOADER_MMC \ + "ext4load mmc ${rootdev}:${bootpart} " + +#ifdef CONFIG_OF_BOARD_SETUP +#define EXTRA_ENV_DTB_RESERVE \ + "dtb_reserve=" \ + "if test -n "$dtb_addr"; then fdt addr $dtb_addr; fi\0" +#else +#define EXTRA_ENV_DTB_RESERVE \ + "dtb_reserve=" \ + "if test -n "$fb_addr"; then " \ + "fdt addr $dtb_addr;" \ + "fdt resize;" \ + "fdt mk /reserved-memory display_reserved;" \ + "fdt set /reserved-memory/display_reserved " \ + "reg <$fb_addr 0x800000>;" \ + "fi;\0" +#endif + +#ifdef CONFIG_SPLASH_SCREEN +#define EXTRA_ENV_BOOT_LOGO \ + "splashimage=" __stringify(BMP_LOAD_ADDR)"\0" \ + "splashfile=" __stringify(SPLASH_FILE)"\0" \ + "splashpos=m,m\0" \ + "fb_addr=\0" \ + EXTRA_ENV_DTB_RESERVE +#else + #define EXTRA_ENV_BOOT_LOGO EXTRA_ENV_DTB_RESERVE +#endif + +#define CONFIG_EXTRA_ENV_SETTINGS \ + "fdt_high=0xffffffff\0" \ + "initrd_high=0xffffffff\0" \ + "rootdev=" __stringify(CONFIG_ROOT_DEV) "\0" \ + "rootpart=" __stringify(CONFIG_ROOT_PART) "\0" \ + "bootpart=" __stringify(CONFIG_BOOT_PART) "\0" \ + "kernel=zImage\0" \ + "loadaddr=" __stringify(CONFIG_SYS_LOAD_ADDR) "\0" \ + "dtb_name=s5p4418-nanopi2-rev01.dtb\0" \ + "dtb_addr=" __stringify(KERNEL_DTB_ADDR) "\0" \ + "initrd_name=ramdisk.img\0" \ + "initrd_addr=" __stringify(INITRD_START) "\0" \ + "initrd_size=0x600000\0" \ + "load_dtb=" \ + BLOADER_MMC "${dtb_addr} ${dtb_name}; " \ + "run dtb_reserve\0" \ + "load_kernel=" \ + BLOADER_MMC "${loadaddr} ${kernel}\0" \ + "load_initrd=" \ + BLOADER_MMC "${initrd_addr} ${initrd_name}; " \ + "setenv initrd_size 0x${filesize}\0" \ + "mmcboot=" \ + "run load_kernel; run load_initrd; run load_dtb; " \ + "bootz ${loadaddr} ${initrd_addr}:${initrd_size} " \ + "${dtb_addr}\0" \ + "bootcmd=run mmcboot\0" \ + EXTRA_ENV_BOOT_LOGO + +#endif /* __CONFIG_H__ */

On Mon, Feb 03, 2020 at 08:39:22PM +0100, Stefan Bosch wrote:
This patch adds support for SAMSUNG's/NEXELL's ARM Cortex-A9 based S5P4418 SoC, especially FriendlyARM's NanoPi2 and NanoPC-T2 boards. It is based on the following FriendlyARM's U-Boot version: https://github.com/friendlyarm/u-boot/tree/nanopi2-v2016.01.
The added mach-nexell also supports the S5P6818 SoC which is a follow-up of the S5P4418.
Main changes in relation to nanopi2-v2016.01:
- Cosmetic changes due to patman warnings/errors.
- MMC and Video drivers changed to DM.
- Configs reworked (e.g. "CONFIG_..." moved from s5p4418_nanopi2.h to s5p4418_nanopi2_defconfig)
- SPL related files are not included.
- MACH_TYPE_S5P4418 is not defined/used anymore.
- arch/arm/mach-nexell/include/mach/boot0.h added to generate the NSIH (Nexell System Information Header), substitudes tools/nexell.
- board/s5p4418/ renamed to board/friendlyarm/
- Only the NanoPi2 and NanoPC-T2 boards are supported yet because I do only have the NanoPC-T2 board to test the code (this board uses the NanoPi2 code).
Stefan Bosch (10): arm: add mach-nexell (header files) arm: add mach-nexell (all files except header files) i2c: mmc: add nexell driver (gpio, i2c, mmc, pwm) video: add nexell video driver (soc: displaytop) video: add nexell video driver (soc: mlc, mipi) video: add nexell video driver (soc: lvds, hdmi) video: add nexell video driver (soc: dpc, makefile) video: add nexell video driver (display/video driver) arm: add support for SoC s5p4418 (cpu) / nanopi2 board arm: add (default) config for nanopi2 board
In general: - Make sure everything is checkpatch clean (or when not, explainable). - Don't include unused (by the end of the series at least) code. - Don't include both SPDX tags and then the whole license boilerplate. - Do make sure that there's nothing in the config.h file that should be in a _defconfig file at this point.
Thanks!

Hi,
On Tue, Feb 4, 2020 at 1:12 AM Stefan Bosch stefan_b@posteo.net wrote:
This patch adds support for SAMSUNG's/NEXELL's ARM Cortex-A9 based S5P4418 SoC, especially FriendlyARM's NanoPi2 and NanoPC-T2 boards. It is based on the following FriendlyARM's U-Boot version: https://github.com/friendlyarm/u-boot/tree/nanopi2-v2016.01.
I don't think this is the right approach, i.e. to take everything from BSP source as it is and put it into mainline U-BOOT. AFAIR, Some of the peripherals present on these NEXELL SoC's are compatible with SAMSUNG IP (for instance the UART). So, are we sure that some of the already existing code in U-BOOT can't be re-used to drive those compatible peripherals at-least ?
Thanks -Amit

Hi Amit,
Am 22.02.20 um 14:06 schrieb Amit Tomer:
Hi,
On Tue, Feb 4, 2020 at 1:12 AM Stefan Bosch stefan_b@posteo.net wrote:
This patch adds support for SAMSUNG's/NEXELL's ARM Cortex-A9 based S5P4418 SoC, especially FriendlyARM's NanoPi2 and NanoPC-T2 boards. It is based on the following FriendlyARM's U-Boot version: https://github.com/friendlyarm/u-boot/tree/nanopi2-v2016.01.
I don't think this is the right approach, i.e. to take everything from BSP source as it is and put it into mainline U-BOOT. AFAIR, Some of the peripherals present on these NEXELL SoC's are compatible with SAMSUNG IP (for instance the UART). So, are we sure that some of the already existing code in U-BOOT can't be re-used to drive those compatible peripherals at-least ?
Thanks -Amit
You are right, already existing code in U-BOOT should be used where possible. So I have reviewed the code (and will review it further). Up to now, I have the following proposals for the peripherials indicated:
UART: Actually the UARTs of the S5P4418 are Amba PrimeCell PL011 compatible, therefore the appropriate code is used. S5P6818 does have different UARTs which apparently arch/arm/mach-nexell/serial.c is for. Since S5P6818 is not supported (yet) I will remove this file.
TIMER: Currently arch/arm/mach-nexell/timer.c is used. I will try to use arch/arm/cpu/armv7/s5p-common/timer.c instead. The timer-registers used seem to be the same, but the functions in timer.c are not which is a possible pitfall.
PWM: Currently drivers/pwm/pwm-nexell.c is used. This is a extended version of arch/arm/cpu/armv7/s5p-common/pwm.c. I.e. pwm.c is adapted with "#if defined(CONFIG_ARCH_NEXELL) ... #else" at some places. So my proposal is to change s5p-common/pwm.c appropriately to get rid of pwm-nexell.c.
Regards Stefan
participants (5)
-
Amit Tomer
-
Heiko Schocher
-
Stefan B.
-
Stefan Bosch
-
Tom Rini