Update Centec platform support for Bullseye and 5.10 kernel (#7)

1. Fix build for armhf and arm64
2. upgrade centec tsingma bsp support to 5.10 kernel
3. modify centec platform driver for linux 5.10

Co-authored-by: Shi Lei <shil@centecnetworks.com>
This commit is contained in:
LuiSzee 2021-10-26 06:36:55 +08:00 committed by Saikrishna Arcot
parent b4dda1c18d
commit 5b284767f6
80 changed files with 8261 additions and 8076 deletions

View File

@ -1,13 +1,13 @@
## Debian mirror for ARM ## Debian mirror for ARM
## Not the repo mirror site can change in future, and needs to be updated to be in sync ## Not the repo mirror site can change in future, and needs to be updated to be in sync
deb [arch=arm64] http://deb.debian.org/debian buster main contrib non-free deb [arch=arm64] http://deb.debian.org/debian bullseye main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian buster main contrib non-free deb-src [arch=arm64] http://deb.debian.org/debian bullseye main contrib non-free
deb [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free deb [arch=arm64] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free deb-src [arch=arm64] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb [arch=arm64] http://ftp.debian.org/debian buster-backports main deb [arch=arm64] http://ftp.debian.org/debian bullseye-backports main
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster main contrib non-free # deb [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye main contrib non-free
deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian buster main contrib non-free # deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye main contrib non-free
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free # deb [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye-updates main contrib non-free
deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free # deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye-updates main contrib non-free
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-backports main # deb [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye-backports main

View File

@ -1,13 +1,13 @@
## Debian mirror for ARM ## Debian mirror for ARM
## Not the repo mirror site can change in future, and needs to be updated to be in sync ## Not the repo mirror site can change in future, and needs to be updated to be in sync
deb [arch=armhf] http://deb.debian.org/debian buster main contrib non-free deb [arch=armhf] http://deb.debian.org/debian bullseye main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian buster main contrib non-free deb-src [arch=armhf] http://deb.debian.org/debian bullseye main contrib non-free
deb [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free deb [arch=armhf] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free deb-src [arch=armhf] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb [arch=armhf] http://ftp.debian.org/debian buster-backports main deb [arch=armhf] http://ftp.debian.org/debian bullseye-backports main
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster main contrib non-free # deb [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye main contrib non-free
deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian buster main contrib non-free # deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye main contrib non-free
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free # deb [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye-updates main contrib non-free
deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free # deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye-updates main contrib non-free
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-backports main # deb [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye-backports main

View File

@ -643,7 +643,7 @@ clean_proc() {
sudo umount /proc || true sudo umount /proc || true
} }
trap_push clean_proc trap_push clean_proc
sudo mount proc /proc -t proc sudo mount proc /proc -t proc || true
sudo mkdir $FILESYSTEM_ROOT/target sudo mkdir $FILESYSTEM_ROOT/target
sudo mount --bind target $FILESYSTEM_ROOT/target sudo mount --bind target $FILESYSTEM_ROOT/target
sudo LANG=C DOCKER_HOST="$DOCKER_HOST" chroot $FILESYSTEM_ROOT docker info sudo LANG=C DOCKER_HOST="$DOCKER_HOST" chroot $FILESYSTEM_ROOT docker info

View File

@ -1,9 +1,9 @@
deb [arch=arm64] http://deb.debian.org/debian buster main contrib non-free deb [arch=arm64] http://deb.debian.org/debian bullseye main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian buster main contrib non-free deb-src [arch=arm64] http://deb.debian.org/debian bullseye main contrib non-free
deb [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free deb [arch=arm64] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb-src [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free deb-src [arch=arm64] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb [arch=arm64] http://security.debian.org buster/updates main contrib non-free deb [arch=arm64] http://security.debian.org bullseye-security main contrib non-free
deb-src [arch=arm64] http://security.debian.org buster/updates main contrib non-free deb-src [arch=arm64] http://security.debian.org bullseye-security main contrib non-free
deb [arch=arm64] https://download.docker.com/linux/debian buster stable deb [arch=arm64] https://download.docker.com/linux/debian bullseye stable
deb [arch=arm64] http://ftp.debian.org/debian buster-backports main deb [arch=arm64] http://ftp.debian.org/debian bullseye-backports main

View File

@ -1,9 +1,9 @@
deb [arch=armhf] http://deb.debian.org/debian buster main contrib non-free deb [arch=armhf] http://deb.debian.org/debian bullseye main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian buster main contrib non-free deb-src [arch=armhf] http://deb.debian.org/debian bullseye main contrib non-free
deb [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free deb [arch=armhf] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb-src [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free deb-src [arch=armhf] http://deb.debian.org/debian bullseye-updates main contrib non-free
deb [arch=armhf] http://security.debian.org buster/updates main contrib non-free deb [arch=armhf] http://security.debian.org bullseye-security main contrib non-free
deb-src [arch=armhf] http://security.debian.org buster/updates main contrib non-free deb-src [arch=armhf] http://security.debian.org bullseye-security main contrib non-free
deb [arch=armhf] https://download.docker.com/linux/debian buster stable deb [arch=armhf] https://download.docker.com/linux/debian bullseye stable
deb [arch=armhf] http://ftp.debian.org/debian buster-backports main deb [arch=armhf] http://ftp.debian.org/debian bullseye-backports main

View File

@ -10,7 +10,6 @@ rtc-sd2405
ctc5236_switch ctc5236_switch
ctc5236_mdio ctc5236_mdio
ctcmac ctcmac
ctcmac_test
ctc5236-mc ctc5236-mc
ctc_wdt ctc_wdt
ehci-ctc ehci-ctc
@ -20,3 +19,4 @@ pwm-ctc
ext4 ext4
overlay overlay
squashfs squashfs
mars

View File

@ -1 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e530_24x2c_platform.o obj-m := centec_e530_24x2c_platform.o

View File

@ -1,7 +1,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/leds.h> #include <linux/leds.h>
@ -129,7 +128,7 @@ static int e530_24x2c_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0); i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0)) if(IS_INVALID_PTR(i2c_client_gpio0))
{ {
i2c_client_gpio0 = NULL; i2c_client_gpio0 = NULL;
@ -145,7 +144,7 @@ static int e530_24x2c_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio1 = i2c_new_device(i2c_adp_gpio1, &i2c_dev_gpio1); i2c_client_gpio1 = i2c_new_client_device(i2c_adp_gpio1, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1)) if(IS_INVALID_PTR(i2c_client_gpio1))
{ {
i2c_client_gpio1 = NULL; i2c_client_gpio1 = NULL;
@ -161,7 +160,7 @@ static int e530_24x2c_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio2 = i2c_new_device(i2c_adp_gpio2, &i2c_dev_gpio2); i2c_client_gpio2 = i2c_new_client_device(i2c_adp_gpio2, &i2c_dev_gpio2);
if(IS_INVALID_PTR(i2c_client_gpio2)) if(IS_INVALID_PTR(i2c_client_gpio2))
{ {
i2c_client_gpio2 = NULL; i2c_client_gpio2 = NULL;

View File

@ -1 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e530_24x2q_platform.o obj-m := centec_e530_24x2q_platform.o

View File

@ -1,7 +1,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/leds.h> #include <linux/leds.h>
@ -129,7 +128,7 @@ static int e530_24x2q_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0); i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0)) if(IS_INVALID_PTR(i2c_client_gpio0))
{ {
i2c_client_gpio0 = NULL; i2c_client_gpio0 = NULL;
@ -145,7 +144,7 @@ static int e530_24x2q_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio1 = i2c_new_device(i2c_adp_gpio1, &i2c_dev_gpio1); i2c_client_gpio1 = i2c_new_client_device(i2c_adp_gpio1, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1)) if(IS_INVALID_PTR(i2c_client_gpio1))
{ {
i2c_client_gpio1 = NULL; i2c_client_gpio1 = NULL;
@ -161,7 +160,7 @@ static int e530_24x2q_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio2 = i2c_new_device(i2c_adp_gpio2, &i2c_dev_gpio2); i2c_client_gpio2 = i2c_new_client_device(i2c_adp_gpio2, &i2c_dev_gpio2);
if(IS_INVALID_PTR(i2c_client_gpio2)) if(IS_INVALID_PTR(i2c_client_gpio2))
{ {
i2c_client_gpio2 = NULL; i2c_client_gpio2 = NULL;

View File

@ -1 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e530_48s4x_platform.o obj-m := centec_e530_48s4x_platform.o

View File

@ -1,7 +1,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/leds.h> #include <linux/leds.h>
@ -197,7 +196,7 @@ static int e530_48s4x_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0); i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0)) if(IS_INVALID_PTR(i2c_client_gpio0))
{ {
i2c_client_gpio0 = NULL; i2c_client_gpio0 = NULL;
@ -213,7 +212,7 @@ static int e530_48s4x_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio1 = i2c_new_device(i2c_adp_gpio1, &i2c_dev_gpio1); i2c_client_gpio1 = i2c_new_client_device(i2c_adp_gpio1, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1)) if(IS_INVALID_PTR(i2c_client_gpio1))
{ {
i2c_client_gpio1 = NULL; i2c_client_gpio1 = NULL;
@ -274,7 +273,7 @@ static int e530_48s4x_init_i2c_epld(void)
return -1; return -1;
} }
i2c_client_epld = i2c_new_device(i2c_adp_master, &i2c_dev_epld); i2c_client_epld = i2c_new_client_device(i2c_adp_master, &i2c_dev_epld);
if(IS_INVALID_PTR(i2c_client_epld)) if(IS_INVALID_PTR(i2c_client_epld))
{ {
i2c_client_epld = NULL; i2c_client_epld = NULL;

View File

@ -1 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e530_48t4x_p_platform.o obj-m := centec_e530_48t4x_p_platform.o

View File

@ -1,7 +1,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_data/pca954x.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/leds.h> #include <linux/leds.h>
@ -195,7 +194,7 @@ static int e530_48t4x_p_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0); i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0)) if(IS_INVALID_PTR(i2c_client_gpio0))
{ {
i2c_client_gpio0 = NULL; i2c_client_gpio0 = NULL;

View File

@ -7,20 +7,20 @@ Standards-Version: 3.9.3
Package: platform-modules-e530-48t4x-p Package: platform-modules-e530-48t4x-p
Architecture: arm64 Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp Description: kernel modules for platform devices such as fan, led, sfp
Package: platform-modules-e530-24x2c Package: platform-modules-e530-24x2c
Architecture: arm64 Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp Description: kernel modules for platform devices such as fan, led, sfp
Package: platform-modules-e530-48s4x Package: platform-modules-e530-48s4x
Architecture: arm64 Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp Description: kernel modules for platform devices such as fan, led, sfp
Package: platform-modules-e530-24x2q Package: platform-modules-e530-24x2q
Architecture: arm64 Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp Description: kernel modules for platform devices such as fan, led, sfp

View File

@ -17,10 +17,6 @@ function install_python_api_package()
device="/usr/share/sonic/device" device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform) platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip2 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip2 install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
fi
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null) rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl) rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
@ -34,6 +30,7 @@ function load_kernel_modules()
ifconfig eth0 hw ether $hwaddr ifconfig eth0 hw ether $hwaddr
fi fi
depmod -a depmod -a
modprobe ctc-i2c-mux-pca954x
modprobe centec_e530_24x2c_platform modprobe centec_e530_24x2c_platform
modprobe fan-ctc5236 modprobe fan-ctc5236
modprobe dal modprobe dal
@ -48,6 +45,7 @@ function remove_kernel_modules()
modprobe -r dal modprobe -r dal
modprobe -r fan-ctc5236 modprobe -r fan-ctc5236
modprobe -r centec_e530_24x2c_platform modprobe -r centec_e530_24x2c_platform
modprobe -r ctc-i2c-mux-pca954x
} }
case "$1" in case "$1" in

View File

@ -1,2 +1 @@
24x2c/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2c-r0
24x2c/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2c-r0 24x2c/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2c-r0

View File

@ -17,10 +17,6 @@ function install_python_api_package()
device="/usr/share/sonic/device" device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform) platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip2 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip2 install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
fi
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null) rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl) rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
@ -34,6 +30,7 @@ function load_kernel_modules()
ifconfig eth0 hw ether $hwaddr ifconfig eth0 hw ether $hwaddr
fi fi
depmod -a depmod -a
modprobe ctc-i2c-mux-pca954x
modprobe centec_e530_24x2q_platform modprobe centec_e530_24x2q_platform
modprobe fan-ctc5236 modprobe fan-ctc5236
modprobe dal modprobe dal
@ -48,6 +45,7 @@ function remove_kernel_modules()
modprobe -r dal modprobe -r dal
modprobe -r fan-ctc5236 modprobe -r fan-ctc5236
modprobe -r centec_e530_24x2q_platform modprobe -r centec_e530_24x2q_platform
modprobe -r ctc-i2c-mux-pca954x
} }
case "$1" in case "$1" in

View File

@ -1,2 +1 @@
24x2q/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2q-r0
24x2q/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2q-r0 24x2q/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_24x2q-r0

View File

@ -17,10 +17,6 @@ function install_python_api_package()
device="/usr/share/sonic/device" device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform) platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip2 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip2 install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
fi
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null) rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl) rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
@ -34,6 +30,7 @@ function load_kernel_modules()
ifconfig eth0 hw ether $hwaddr ifconfig eth0 hw ether $hwaddr
fi fi
depmod -a depmod -a
modprobe ctc-i2c-mux-pca954x
modprobe centec_e530_48s4x_platform modprobe centec_e530_48s4x_platform
modprobe fan-ctc5236 modprobe fan-ctc5236
modprobe dal modprobe dal
@ -48,6 +45,7 @@ function remove_kernel_modules()
modprobe -r dal modprobe -r dal
modprobe -r fan-ctc5236 modprobe -r fan-ctc5236
modprobe -r centec_e530_48s4x_platform modprobe -r centec_e530_48s4x_platform
modprobe -r ctc-i2c-mux-pca954x
} }
case "$1" in case "$1" in

View File

@ -1,2 +1 @@
48s4x/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/arm64-centec_e530_48s4x-r0
48s4x/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_48s4x-r0 48s4x/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_48s4x-r0

View File

@ -17,10 +17,6 @@ function install_python_api_package()
device="/usr/share/sonic/device" device="/usr/share/sonic/device"
platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform) platform=$(/usr/local/bin/sonic-cfggen -H -v DEVICE_METADATA.localhost.platform)
rv=$(pip2 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then
rv=$(pip2 install $device/$platform/sonic_platform-1.0-py2-none-any.whl)
fi
rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null) rv=$(pip3 show sonic-platform > /dev/null 2>/dev/null)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl) rv=$(pip3 install $device/$platform/sonic_platform-1.0-py3-none-any.whl)
@ -34,6 +30,7 @@ function load_kernel_modules()
ifconfig eth0 hw ether $hwaddr ifconfig eth0 hw ether $hwaddr
fi fi
depmod -a depmod -a
modprobe ctc-i2c-mux-pca954x
modprobe centec_e530_48t4x_p_platform modprobe centec_e530_48t4x_p_platform
modprobe fan-ctc5236 modprobe fan-ctc5236
modprobe dal modprobe dal
@ -48,6 +45,7 @@ function remove_kernel_modules()
modprobe -r dal modprobe -r dal
modprobe -r fan-ctc5236 modprobe -r fan-ctc5236
modprobe -r centec_e530_48t4x_p_platform modprobe -r centec_e530_48t4x_p_platform
modprobe -r ctc-i2c-mux-pca954x
} }
case "$1" in case "$1" in

View File

@ -1,2 +1 @@
48t4x-p/modules/sonic_platform-1.0-py2-none-any.whl usr/share/sonic/device/arm64-centec_e530_48t4x_p-r0
48t4x-p/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_48t4x_p-r0 48t4x-p/modules/sonic_platform-1.0-py3-none-any.whl usr/share/sonic/device/arm64-centec_e530_48t4x_p-r0

View File

@ -24,6 +24,7 @@ CLASSES_DIR := classes
CONF_DIR := conf CONF_DIR := conf
KDAL_DIR := ../../centec/centec-dal/ KDAL_DIR := ../../centec/centec-dal/
FAN_DIR := fan FAN_DIR := fan
PCA954X_DIR := pca954x
%: %:
dh $@ dh $@
@ -41,10 +42,12 @@ build:
(for mod in $(FAN_DIR); do \ (for mod in $(FAN_DIR); do \
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \ make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \
done) done)
(for mod in $(PCA954X_DIR); do \
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \
done)
(for mod in $(MODULE_DIRS); do \ (for mod in $(MODULE_DIRS); do \
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
cd $${mod}; \ cd $${mod}; \
python2.7 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \ python3 setup.py bdist_wheel -d $(MOD_SRC_DIR)/$${mod}/modules; \
cd -; \ cd -; \
done) done)
@ -73,6 +76,7 @@ binary-indep:
cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ cp $(MOD_SRC_DIR)/$${mod}/$(MODULE_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp $(MOD_SRC_DIR)/$(KDAL_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ cp $(MOD_SRC_DIR)/$(KDAL_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp $(MOD_SRC_DIR)/$(FAN_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ cp $(MOD_SRC_DIR)/$(FAN_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp $(MOD_SRC_DIR)/$(PCA954X_DIR)/*.ko debian/$(PACKAGE_PRE_NAME)-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \ cp $(MOD_SRC_DIR)/$${mod}/$(SERVICE_DIR)/*.service debian/$(PACKAGE_PRE_NAME)-$${mod}/lib/systemd/system/; \
done) done)
# Resuming debhelper scripts # Resuming debhelper scripts

View File

@ -0,0 +1 @@
obj-m := ctc-i2c-mux-pca954x.o

View File

@ -0,0 +1,581 @@
// SPDX-License-Identifier: GPL-2.0
/*
* I2C multiplexer
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
*
* This module supports the PCA954x and PCA984x series of I2C multiplexer/switch
* chips made by NXP Semiconductors.
* This includes the:
* PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547,
* PCA9548, PCA9846, PCA9847, PCA9848 and PCA9849.
*
* These chips are all controlled via the I2C bus itself, and all have a
* single 8-bit register. The upstream "parent" bus fans out to two,
* four, or eight downstream busses or channels; which of these
* are selected is determined by the chip type and register contents. A
* mux can select only one sub-bus at a time; a switch can select any
* combination simultaneously.
*
* Based on:
* pca954x.c from Kumar Gala <galak@kernel.crashing.org>
* Copyright (C) 2006
*
* Based on:
* pca954x.c from Ken Harrenstien
* Copyright (C) 2004 Google, Inc. (Ken Harrenstien)
*
* Based on:
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
* and
* pca9540.c from Jean Delvare <jdelvare@suse.de>.
*/
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <dt-bindings/mux/mux.h>
#include "ctc-pca954x.h"
#define PCA954X_MAX_NCHANS 8
#define PCA954X_IRQ_OFFSET 4
enum pca_type {
pca_9540,
pca_9542,
pca_9543,
pca_9544,
pca_9545,
pca_9546,
pca_9547,
pca_9548,
pca_9846,
pca_9847,
pca_9848,
pca_9849,
};
struct chip_desc {
u8 nchans;
u8 enable; /* used for muxes only */
u8 has_irq;
enum muxtype {
pca954x_ismux = 0,
pca954x_isswi
} muxtype;
struct i2c_device_identity id;
};
struct pca954x {
const struct chip_desc *chip;
u8 last_chan; /* last register value */
/* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
s32 idle_state;
struct i2c_client *client;
struct irq_domain *irq;
unsigned int irq_mask;
raw_spinlock_t lock;
};
/* Provide specs for the PCA954x types we know about */
static const struct chip_desc chips[] = {
[pca_9540] = {
.nchans = 2,
.enable = 0x4,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9542] = {
.nchans = 2,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9543] = {
.nchans = 2,
.has_irq = 1,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9544] = {
.nchans = 4,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9545] = {
.nchans = 4,
.has_irq = 1,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9546] = {
.nchans = 4,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9547] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9548] = {
.nchans = 8,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9846] = {
.nchans = 4,
.muxtype = pca954x_isswi,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x10b,
},
},
[pca_9847] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x108,
},
},
[pca_9848] = {
.nchans = 8,
.muxtype = pca954x_isswi,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x10a,
},
},
[pca_9849] = {
.nchans = 4,
.enable = 0x4,
.muxtype = pca954x_ismux,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x109,
},
},
};
static const struct i2c_device_id pca954x_id[] = {
{ "ctc_pca9540", pca_9540 },
{ "ctc_pca9542", pca_9542 },
{ "ctc_pca9543", pca_9543 },
{ "ctc_pca9544", pca_9544 },
{ "ctc_pca9545", pca_9545 },
{ "ctc_pca9546", pca_9546 },
{ "ctc_pca9547", pca_9547 },
{ "ctc_pca9548", pca_9548 },
{ "ctc_pca9846", pca_9846 },
{ "ctc_pca9847", pca_9847 },
{ "ctc_pca9848", pca_9848 },
{ "ctc_pca9849", pca_9849 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca954x_id);
static const struct of_device_id pca954x_of_match[] = {
{ .compatible = "nxp,ctc_pca9540", .data = &chips[pca_9540] },
{ .compatible = "nxp,ctc_pca9542", .data = &chips[pca_9542] },
{ .compatible = "nxp,ctc_pca9543", .data = &chips[pca_9543] },
{ .compatible = "nxp,ctc_pca9544", .data = &chips[pca_9544] },
{ .compatible = "nxp,ctc_pca9545", .data = &chips[pca_9545] },
{ .compatible = "nxp,ctc_pca9546", .data = &chips[pca_9546] },
{ .compatible = "nxp,ctc_pca9547", .data = &chips[pca_9547] },
{ .compatible = "nxp,ctc_pca9548", .data = &chips[pca_9548] },
{ .compatible = "nxp,ctc_pca9846", .data = &chips[pca_9846] },
{ .compatible = "nxp,ctc_pca9847", .data = &chips[pca_9847] },
{ .compatible = "nxp,ctc_pca9848", .data = &chips[pca_9848] },
{ .compatible = "nxp,ctc_pca9849", .data = &chips[pca_9849] },
{}
};
MODULE_DEVICE_TABLE(of, pca954x_of_match);
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
for this as they will try to lock adapter a second time */
static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
union i2c_smbus_data dummy;
return __i2c_smbus_xfer(adap, client->addr, client->flags,
I2C_SMBUS_WRITE, val,
I2C_SMBUS_BYTE, &dummy);
}
static u8 pca954x_regval(struct pca954x *data, u8 chan)
{
/* We make switches look like muxes, not sure how to be smarter. */
if (data->chip->muxtype == pca954x_ismux)
return chan | data->chip->enable;
else
return 1 << chan;
}
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
u8 regval;
int ret = 0;
regval = pca954x_regval(data, chan);
/* Only select the channel if its different from the last channel */
if (data->last_chan != regval) {
ret = pca954x_reg_write(muxc->parent, client, regval);
data->last_chan = ret < 0 ? 0 : regval;
}
return ret;
}
static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
s32 idle_state;
idle_state = READ_ONCE(data->idle_state);
if (idle_state >= 0)
/* Set the mux back to a predetermined channel */
return pca954x_select_chan(muxc, idle_state);
if (idle_state == MUX_IDLE_DISCONNECT) {
/* Deselect active channel */
data->last_chan = 0;
return pca954x_reg_write(muxc->parent, client,
data->last_chan);
}
/* otherwise leave as-is */
return 0;
}
static ssize_t idle_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
}
static ssize_t idle_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int val;
int ret;
ret = kstrtoint(buf, 0, &val);
if (ret < 0)
return ret;
if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
(val < 0 || val >= data->chip->nchans))
return -EINVAL;
i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
WRITE_ONCE(data->idle_state, val);
/*
* Set the mux into a state consistent with the new
* idle_state.
*/
if (data->last_chan || val != MUX_IDLE_DISCONNECT)
ret = pca954x_deselect_mux(muxc, 0);
i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR_RW(idle_state);
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
{
struct pca954x *data = dev_id;
unsigned long pending;
int ret, i;
ret = i2c_smbus_read_byte(data->client);
if (ret < 0)
return IRQ_NONE;
pending = (ret >> PCA954X_IRQ_OFFSET) & (BIT(data->chip->nchans) - 1);
for_each_set_bit(i, &pending, data->chip->nchans)
handle_nested_irq(irq_linear_revmap(data->irq, i));
return IRQ_RETVAL(pending);
}
static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
{
if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW)
return -EINVAL;
return 0;
}
static struct irq_chip pca954x_irq_chip = {
.name = "i2c-mux-pca954x",
.irq_set_type = pca954x_irq_set_type,
};
static int pca954x_irq_setup(struct i2c_mux_core *muxc)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
int c, irq;
if (!data->chip->has_irq || client->irq <= 0)
return 0;
raw_spin_lock_init(&data->lock);
data->irq = irq_domain_add_linear(client->dev.of_node,
data->chip->nchans,
&irq_domain_simple_ops, data);
if (!data->irq)
return -ENODEV;
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_create_mapping(data->irq, c);
if (!irq) {
dev_err(&client->dev, "failed irq create map\n");
return -EINVAL;
}
irq_set_chip_data(irq, data);
irq_set_chip_and_handler(irq, &pca954x_irq_chip,
handle_simple_irq);
}
return 0;
}
static void pca954x_cleanup(struct i2c_mux_core *muxc)
{
struct pca954x *data = i2c_mux_priv(muxc);
int c, irq;
if (data->irq) {
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_find_mapping(data->irq, c);
irq_dispose_mapping(irq);
}
irq_domain_remove(data->irq);
}
i2c_mux_del_adapters(muxc);
}
static int pca954x_init(struct i2c_client *client, struct pca954x *data)
{
int ret;
if (data->idle_state >= 0)
data->last_chan = pca954x_regval(data, data->idle_state);
else
data->last_chan = 0; /* Disconnect multiplexer */
ret = i2c_smbus_write_byte(client, data->last_chan);
if (ret < 0)
data->last_chan = 0;
return ret;
}
/*
* I2C init/probing/exit functions
*/
static int pca954x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adap = client->adapter;
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
struct device *dev = &client->dev;
struct gpio_desc *gpio;
int num, force, class;
struct i2c_mux_core *muxc;
struct pca954x *data;
int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
return -ENODEV;
muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0,
pca954x_select_chan, pca954x_deselect_mux);
if (!muxc)
return -ENOMEM;
data = i2c_mux_priv(muxc);
i2c_set_clientdata(client, muxc);
data->client = client;
/* Reset the mux if a reset GPIO is specified. */
gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
if (gpio) {
udelay(1);
gpiod_set_value_cansleep(gpio, 0);
/* Give the chip some time to recover. */
udelay(1);
}
data->chip = device_get_match_data(dev);
if (!data->chip)
data->chip = &chips[id->driver_data];
if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) {
struct i2c_device_identity id;
ret = i2c_get_device_id(client, &id);
if (ret && ret != -EOPNOTSUPP)
return ret;
if (!ret &&
(id.manufacturer_id != data->chip->id.manufacturer_id ||
id.part_id != data->chip->id.part_id)) {
dev_warn(dev, "unexpected device id %03x-%03x-%x\n",
id.manufacturer_id, id.part_id,
id.die_revision);
return -ENODEV;
}
}
data->idle_state = MUX_IDLE_AS_IS;
if (device_property_read_u32(dev, "idle-state", &data->idle_state)) {
if (device_property_read_bool(dev, "i2c-mux-idle-disconnect"))
data->idle_state = MUX_IDLE_DISCONNECT;
}
/*
* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to a channel
* or disconnected state.
*/
ret = pca954x_init(client, data);
if (ret < 0) {
dev_warn(dev, "probe failed\n");
return -ENODEV;
}
ret = pca954x_irq_setup(muxc);
if (ret)
goto fail_cleanup;
/* Now create an adapter for each channel */
for (num = 0; num < data->chip->nchans; num++) {
force = 0; /* dynamic adap number */
class = 0; /* no class by default */
if (pdata) {
if (num < pdata->num_modes) {
/* force static number */
force = pdata->modes[num].adap_id;
class = pdata->modes[num].class;
} else
/* discard unconfigured channels */
break;
}
ret = i2c_mux_add_adapter(muxc, force, num, class);
if (ret)
goto fail_cleanup;
}
if (data->irq) {
ret = devm_request_threaded_irq(dev, data->client->irq,
NULL, pca954x_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
"pca954x", data);
if (ret)
goto fail_cleanup;
}
/*
* The attr probably isn't going to be needed in most cases,
* so don't fail completely on error.
*/
device_create_file(dev, &dev_attr_idle_state);
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
num, data->chip->muxtype == pca954x_ismux
? "mux" : "switch", client->name);
return 0;
fail_cleanup:
pca954x_cleanup(muxc);
return ret;
}
static int pca954x_remove(struct i2c_client *client)
{
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
device_remove_file(&client->dev, &dev_attr_idle_state);
pca954x_cleanup(muxc);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int pca954x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int ret;
ret = pca954x_init(client, data);
if (ret < 0)
dev_err(&client->dev, "failed to verify mux presence\n");
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume);
static struct i2c_driver pca954x_driver = {
.driver = {
.name = "ctc_pca954x",
.pm = &pca954x_pm,
.of_match_table = pca954x_of_match,
},
.probe = pca954x_probe,
.remove = pca954x_remove,
.id_table = pca954x_id,
};
module_i2c_driver(pca954x_driver);
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("PCA954x I2C mux/switch driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,48 @@
/*
*
* pca954x.h - I2C multiplexer/switch support
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@xxxxxxxx>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@xxxxxxxxxxx>
* Michael Lawnick <michael.lawnick.ext@xxxxxxx>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _LINUX_I2C_PCA954X_H
#define _LINUX_I2C_PCA954X_H
/* Platform data for the PCA954x I2C multiplexers */
/* Per channel initialisation data:
* @adap_id: bus number for the adapter. 0 = don't care
* @deselect_on_exit: set this entry to 1, if your H/W needs deselection
* of this channel after transaction.
*
*/
struct pca954x_platform_mode {
int adap_id;
unsigned int deselect_on_exit:1;
unsigned int class;
};
/* Per mux/switch data, used with i2c_register_board_info */
struct pca954x_platform_data {
struct pca954x_platform_mode *modes;
int num_modes;
};
#endif /* _LINUX_I2C_PCA954X_H */

View File

@ -12,7 +12,7 @@
images { images {
kernel_ctc { kernel_ctc {
description = "ARM64 Kernel"; description = "ARM64 Kernel";
data = /incbin/("./vmlinuz-4.19.0-12-2-arm64"); data = /incbin/("./vmlinuz-5.10.0-8-2-arm64");
type = "kernel"; type = "kernel";
arch = "arm64"; arch = "arm64";
os = "linux"; os = "linux";
@ -25,7 +25,7 @@
}; };
initramfs { initramfs {
description = "initramfs"; description = "initramfs";
data = /incbin/("./initrd.img-4.19.0-12-2-arm64"); data = /incbin/("./initrd.img-5.10.0-8-2-arm64");
type = "ramdisk"; type = "ramdisk";
arch = "arm64"; arch = "arm64";
os = "linux"; os = "linux";

View File

@ -7,5 +7,5 @@ Standards-Version: 3.9.3
Package: tsingma-bsp Package: tsingma-bsp
Architecture: arm64 Architecture: arm64
Depends: linux-image-4.19.0-12-2-arm64-unsigned Depends: linux-image-5.10.0-8-2-arm64-unsigned
Description: kernel modules for tsingma bsp Description: kernel modules for tsingma bsp

2
platform/centec-arm64/tsingma-bsp/debian/rules Normal file → Executable file
View File

@ -15,7 +15,7 @@ PACKAGE_PRE_NAME := tsingma-bsp
KVERSION ?= $(shell uname -r) KVERSION ?= $(shell uname -r)
KERNEL_SRC := /lib/modules/$(KVERSION) KERNEL_SRC := /lib/modules/$(KVERSION)
MOD_SRC_DIR:= $(shell pwd) MOD_SRC_DIR:= $(shell pwd)
MODULE_DIRS:= ctc5236-mc ctc5236_switch ctcmac ctc_wdt ehci-ctc gpio-ctc i2c-ctc pinctrl-ctc pwm-ctc rtc-sd2405 sdhci-ctc5236 spi-ctc-qspi MODULE_DIRS:= ctc5236-mc ctc5236_switch ctcmac ctc_wdt ehci-ctc gpio-ctc i2c-ctc pinctrl-ctc pwm-ctc rtc-sd2405 sdhci-ctc5236 spi-ctc-qspi ctc-phy
DTS_DIR := ctc-dts DTS_DIR := ctc-dts
MODULE_DIR := src MODULE_DIR := src
UTILS_DIR := utils UTILS_DIR := utils

View File

@ -1,17 +1,17 @@
src/ctc5236-mc/ctc5236-mc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/ctc5236-mc/ctc5236-mc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/pwm-ctc/pwm-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/pwm-ctc/pwm-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctc5236_switch/ctc5236_switch.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/ctc5236_switch/ctc5236_switch.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/pinctrl-ctc/pinctrl-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/pinctrl-ctc/pinctrl-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctc_wdt/ctc_wdt.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/ctc_wdt/ctc_wdt.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctcmac/ctcmac.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/ctcmac/ctcmac.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctcmac/ctcmac_test.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/ctcmac/ctc5236_mdio.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctcmac/ctc5236_mdio.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/ctc-phy/mars.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/i2c-ctc/i2c-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/i2c-ctc/i2c-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/gpio-ctc/gpio-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/gpio-ctc/gpio-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ehci-ctc/ehci-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/ehci-ctc/ehci-ctc.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/rtc-sd2405/rtc-sd2405.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/rtc-sd2405/rtc-sd2405.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/sdhci-ctc5236/sdhci-ctc5236.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/sdhci-ctc5236/sdhci-ctc5236.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/spi-ctc-qspi/spi-ctc-qspi.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra src/spi-ctc-qspi/spi-ctc-qspi.ko /lib/modules/5.10.0-8-2-arm64/kernel/extra
src/ctc-dts/e530-ctc5236.dtb /boot/ src/ctc-dts/e530-ctc5236.dtb /boot/
src/config/fw_env.config /etc/ src/config/fw_env.config /etc/
src/config/tsingma-bsp.service /lib/systemd/system src/config/tsingma-bsp.service /lib/systemd/system

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* /*
* This header provides constants for the ARM GIC. * This header provides constants for the ARM GIC.
*/ */

View File

@ -88,7 +88,11 @@
memory-controller@30600000 { memory-controller@30600000 {
compatible = "ctc,ctc5236-ddr-ctrl"; compatible = "ctc,ctc5236-ddr-ctrl";
reg = <0x0 0x30600000 0x0 0x100000>; reg = <0x0 0x30600000 0x0 0x100000>;
interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 100 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 101 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
ctc,sysctrl = <&sysctrl>;
}; };
sysctrl: sysctrl@33200000 { sysctrl: sysctrl@33200000 {
@ -136,12 +140,15 @@
#size-cells = <2>; #size-cells = <2>;
interrupt-parent = <&gic>; interrupt-parent = <&gic>;
status = "disabled"; status = "disabled";
local-mac-address = [00 00 00 00 00 00];
index = <0x00>; index = <0x00>;
reg = <0x0 0x33410000 0x0 0x10000>, reg = <0x0 0x33410000 0x0 0x10000>,
<0x0 0x33400000 0x0 0x10000>; <0x0 0x33400000 0x0 0x10000>;
interrupts = <GIC_SPI 40 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>, interrupts = <GIC_SPI 40 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 41 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>, <GIC_SPI 41 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 44 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>; <GIC_SPI 44 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 136 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 102 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
ctc,sysctrl = <&sysctrl>; ctc,sysctrl = <&sysctrl>;
}; };
@ -152,12 +159,15 @@
#size-cells = <2>; #size-cells = <2>;
interrupt-parent = <&gic>; interrupt-parent = <&gic>;
status = "disabled"; status = "disabled";
local-mac-address = [00 00 00 00 00 00];
index = <0x01>; index = <0x01>;
reg = <0x0 0x33420000 0x0 0x10000>, reg = <0x0 0x33420000 0x0 0x10000>,
<0x0 0x33400000 0x0 0x10000>; <0x0 0x33400000 0x0 0x10000>;
interrupts = <GIC_SPI 42 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>, interrupts = <GIC_SPI 42 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 43 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>, <GIC_SPI 43 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 44 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>; <GIC_SPI 44 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 137 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>,
<GIC_SPI 103 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_HIGH)>;
ctc,sysctrl = <&sysctrl>; ctc,sysctrl = <&sysctrl>;
}; };
@ -165,6 +175,7 @@
compatible = "ctc-ehci"; compatible = "ctc-ehci";
reg = <0x0 0x30500000 0x0 0x1000>; reg = <0x0 0x30500000 0x0 0x1000>;
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
ctc,sysctrl = <&sysctrl>;
status = "disabled"; status = "disabled";
}; };
@ -172,7 +183,7 @@
compatible = "generic-ohci"; compatible = "generic-ohci";
reg = <0x0 0x30580000 0x0 0x1000>; reg = <0x0 0x30580000 0x0 0x1000>;
interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled"; status = "okay";
}; };
spi: spi@33100000 { spi: spi@33100000 {
@ -188,7 +199,7 @@
status ="disabled"; status ="disabled";
}; };
qspi: qspi@10000000 { qspi: spi@10000000 {
compatible = "ctc, igdaxi001a-qspi"; compatible = "ctc, igdaxi001a-qspi";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
@ -228,6 +239,8 @@
reg = <0x0 0x33700000 0x0 0x1000>; reg = <0x0 0x33700000 0x0 0x1000>;
interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2c_clk>; clocks = <&i2c_clk>;
ctc,sysctrl = <&sysctrl>;
i2c-num = <0>;
status ="disabled"; status ="disabled";
}; };
@ -238,6 +251,8 @@
reg = <0x0 0x33701000 0x0 0x1000>; reg = <0x0 0x33701000 0x0 0x1000>;
interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>; interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&i2c_clk>; clocks = <&i2c_clk>;
ctc,sysctrl = <&sysctrl>;
i2c-num = <1>;
status ="disabled"; status ="disabled";
}; };
@ -257,7 +272,8 @@
interrupt-names = "msi","aer","pme"; interrupt-names = "msi","aer","pme";
msi-parent = <&pcie>; msi-parent = <&pcie>;
bus-range = <0 0xff>; bus-range = <0 0xff>;
ranges = <0x43000000 0 0x00000000 0 0x40000000 0 0x40000000>; ranges = <0x42000000 0 0x00000000 0 0x40000000 0 0x20000000
0x02000000 0 0x20000000 0 0x60000000 0 0x20000000>;
num-lanes = <1>; num-lanes = <1>;
ctc,sysctrl = <&sysctrl>; ctc,sysctrl = <&sysctrl>;
status ="disabled"; status ="disabled";
@ -338,6 +354,7 @@
reg = <0x0 0x33610000 0x0 0x10000>; reg = <0x0 0x33610000 0x0 0x10000>;
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
ctc,sysctrl = <&sysctrl>;
porta: gpio-port@0 { porta: gpio-port@0 {
compatible = "ctc,apb-gpio-porta"; compatible = "ctc,apb-gpio-porta";
@ -368,7 +385,42 @@
ctc,pinctrl-bank0 = <16>; ctc,pinctrl-bank0 = <16>;
ctc,pinctrl-bank1 = <8>; ctc,pinctrl-bank1 = <8>;
ctc,sysctrl = <&sysctrl>; ctc,sysctrl = <&sysctrl>;
status = "okay";
spi0 {
spi_pin: spi_pin {
ctc,pins = <0 0 PIN_FUNC_SPI>,
<0 2 PIN_FUNC_SPI>,
<0 3 PIN_FUNC_SPI>,
<0 4 PIN_FUNC_SPI>,
<0 5 PIN_FUNC_SPI>,
<0 6 PIN_FUNC_SPI>,
<0 7 PIN_FUNC_SPI>;
};
};
uart2 {
uart2_pin: uart2_pin {
ctc,pins = <0 10 PIN_FUNC_UART>,
<0 11 PIN_FUNC_UART>,
<0 12 PIN_FUNC_UART>,
<0 13 PIN_FUNC_UART>,
<0 14 PIN_FUNC_UART>,
<0 15 PIN_FUNC_UART>;
};
};
fc {
fc_pin: fc_pin {
ctc,pins = <1 0 PIN_FUNC_FC>,
<1 1 PIN_FUNC_FC>,
<1 2 PIN_FUNC_FC>,
<1 3 PIN_FUNC_FC>,
<1 4 PIN_FUNC_FC>,
<1 5 PIN_FUNC_FC>,
<1 6 PIN_FUNC_FC>,
<1 7 PIN_FUNC_FC>;
};
};
}; };
}; };

View File

@ -157,6 +157,7 @@
non-removable; non-removable;
no-sd; no-sd;
no-sdio; no-sdio;
cap-mmc-hw-reset;
voltage-ranges = <3300 3300>; voltage-ranges = <3300 3300>;
status = "okay"; status = "okay";
}; };
@ -187,7 +188,7 @@
&pinctrl { &pinctrl {
spi { spi0 {
spi_pin: spi_pin { spi_pin: spi_pin {
status = "disabled"; status = "disabled";
ctc,pins = <0 0 PIN_FUNC_SPI>, ctc,pins = <0 0 PIN_FUNC_SPI>,

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
/* /*
* This header provides constants for most IRQ bindings. * This header provides constants for most IRQ bindings.
* *

View File

@ -0,0 +1 @@
obj-m = mars.o

View File

@ -0,0 +1,316 @@
/*
* drivers/net/phy/mars.c
*
* Driver for Centec PHYs
*
* Author: liuht
*
* Copyright 2002-2018, Centec Networks (Suzhou) Co., Ltd.
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/of.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <linux/uaccess.h>
/* Mask used for ID comparisons */
#define CTC_PHY_ID_MASK 0xffffffff
/* Known PHY IDs */
#define CTC_PHY_ID_MARS1S_V1 0x00782013
#define CTC_PHY_ID_MARS1S 0x01E04013
#define CTC_PHY_ID_MARS1P_V1 0x00782011
#define CTC_PHY_ID_MARS1P 0x01E04011
#define CTC_PHY_IMASK 0x12
#define CTC_PHY_IEVENT 0x13
#define CTC_PHY_IMASK_INIT 0x6c00
#define CTC_PHY_IMASK_CLEAR 0x0000
#define CTC_PHY_REG_SPACE 0
#define CTC_SDS_REG_SPACE 1
static int mars_ext_read(struct phy_device *phydev, u32 regnum)
{
int ret;
ret = phy_write(phydev, 0x1e, regnum);
if (ret < 0)
return ret;
return phy_read(phydev, 0x1f);
}
static int mars_ext_write(struct phy_device *phydev, u32 regnum, u16 val)
{
int ret;
ret = phy_write(phydev, 0x1e, regnum);
if (ret < 0)
return ret;
return phy_write(phydev, 0x1f, val);
}
static int mars_select_reg_space(struct phy_device *phydev, int space)
{
int ret;
if (space == CTC_PHY_REG_SPACE) {
ret = mars_ext_write(phydev, 0xa000, 0x0);
} else {
ret = mars_ext_write(phydev, 0xa000, 0x2);
}
return ret;
}
static int mars_config_advert(struct phy_device *phydev)
{
int err, bmsr, changed = 0;
u32 adv;
/* Only allow advertising what this PHY supports */
linkmode_and(phydev->advertising, phydev->advertising,
phydev->supported);
adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
/* Setup standard advertisement */
err = phy_modify_changed(phydev, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_100BASE4 |
ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
adv);
if (err < 0)
return err;
if (err > 0)
changed = 1;
bmsr = phy_read(phydev, MII_BMSR);
if (bmsr < 0)
return bmsr;
/* Per 802.3-2008, Section 22.2.4.2.16 Extended status all
* 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a
* logical 1.
*/
if (!(bmsr & BMSR_ESTATEN))
return changed;
adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
err = phy_modify_changed(phydev, MII_CTRL1000,
ADVERTISE_1000FULL | ADVERTISE_1000HALF, adv);
if (err < 0)
return err;
if (err > 0)
changed = 1;
return changed;
}
int mars1s_config_aneg(struct phy_device *phydev)
{
int err, changed = 0;
if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev);
err = mars_config_advert(phydev);
if (err < 0) /* error */
return err;
changed |= err;
if (changed == 0) {
/* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
*/
int ctl = phy_read(phydev, MII_BMCR);
if (ctl < 0)
return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
changed = 1; /* do restart aneg */
}
/* Only restart aneg if we are advertising something different
* than we were before.
*/
if (changed > 0)
return genphy_restart_aneg(phydev);
return 0;
}
static int mars_ack_interrupt(struct phy_device *phydev)
{
int err;
#if 1
/* Clear the interrupts by reading the reg */
err = phy_read(phydev, CTC_PHY_IEVENT);
#else
err = mars_ext_read(phydev, 0xa011);
#endif
if (err < 0)
return err;
return 0;
}
static int mars_config_intr(struct phy_device *phydev)
{
int err;
#if 1
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, CTC_PHY_IMASK, CTC_PHY_IMASK_INIT);
else
err = phy_write(phydev, CTC_PHY_IMASK, CTC_PHY_IMASK_CLEAR);
#else
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = mars_ext_write(phydev, 0xa010, 0xffff);
else
err = mars_ext_write(phydev, 0xa010, 0x0000);
#endif
return err;
}
#if 0
static int mars_set_link_timer_6_3ms(struct phy_device *phydev)
{
int ret = 0;
ret = mars_select_reg_space(phydev, CTC_SDS_REG_SPACE);
if (!ret)
mars_ext_write(phydev, 0xa5, 0xc);
mars_select_reg_space(phydev, CTC_PHY_REG_SPACE);
return 0;
}
#endif
static int mars_set_link_timer_2_6ms(struct phy_device *phydev)
{
int ret = 0;
ret = mars_select_reg_space(phydev, CTC_SDS_REG_SPACE);
if (!ret)
mars_ext_write(phydev, 0xa5, 0x5);
mars_select_reg_space(phydev, CTC_PHY_REG_SPACE);
return 0;
}
int mars_config_init(struct phy_device *phydev)
{
return mars_set_link_timer_2_6ms(phydev);
}
int mars1p_config_init(struct phy_device *phydev)
{
/*RGMII clock 2.5M when link down, bit12:1->0 */
mars_ext_write(phydev, 0xc, 0x8051);
/*Disable sleep mode, bit15:1->0 */
mars_ext_write(phydev, 0x27, 0x2029);
/* disable PHY to respond to MDIO access with PHYAD0 */
/* MMD7 8001h: bit6: 0, change value: 0x7f --> 0x3f */
phy_write(phydev, 0xd, 0x7);
phy_write(phydev, 0xe, 0x8001);
phy_write(phydev, 0xd, 0x4007);
phy_write(phydev, 0xe, 0x3f);
return mars_set_link_timer_2_6ms(phydev);
}
static struct phy_driver ctc_drivers[] = {
{
.phy_id = CTC_PHY_ID_MARS1S,
.phy_id_mask = CTC_PHY_ID_MASK,
.name = "CTC MARS1S",
.config_init = mars_config_init,
.features = PHY_GBIT_FEATURES,
.config_aneg = mars1s_config_aneg,
.ack_interrupt = &mars_ack_interrupt,
.config_intr = &mars_config_intr,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
{
.phy_id = CTC_PHY_ID_MARS1S_V1,
.phy_id_mask = CTC_PHY_ID_MASK,
.name = "CTC MARS1S_V1",
.config_init = mars_config_init,
.features = PHY_GBIT_FEATURES,
.config_aneg = mars1s_config_aneg,
.ack_interrupt = &mars_ack_interrupt,
.config_intr = &mars_config_intr,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
{
.phy_id = CTC_PHY_ID_MARS1P,
.phy_id_mask = CTC_PHY_ID_MASK,
.name = "CTC MARS1P",
.config_init = mars1p_config_init,
.features = PHY_GBIT_FEATURES,
.config_aneg = mars1s_config_aneg,
.ack_interrupt = &mars_ack_interrupt,
.config_intr = &mars_config_intr,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
{
.phy_id = CTC_PHY_ID_MARS1P_V1,
.phy_id_mask = CTC_PHY_ID_MASK,
.name = "CTC MARS1P_V1",
.config_init = mars1p_config_init,
.features = PHY_GBIT_FEATURES,
.config_aneg = mars1s_config_aneg,
.ack_interrupt = &mars_ack_interrupt,
.config_intr = &mars_config_intr,
.read_status = genphy_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
};
module_phy_driver(ctc_drivers);
static struct mdio_device_id __maybe_unused mars_tbl[] = {
{CTC_PHY_ID_MARS1S, CTC_PHY_ID_MASK},
{CTC_PHY_ID_MARS1S_V1, CTC_PHY_ID_MASK},
{CTC_PHY_ID_MARS1P, CTC_PHY_ID_MASK},
{CTC_PHY_ID_MARS1P_V1, CTC_PHY_ID_MASK},
{}
};
MODULE_DEVICE_TABLE(mdio, mars_tbl);

View File

@ -1,4 +1,5 @@
/* Centec TsingMa Memory Controller Driver /*
* Centec TsingMa Memory Contoller Driver
* *
* Author: lius <lius@centecnetworks.com> * Author: lius <lius@centecnetworks.com>
* *
@ -22,22 +23,29 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/io.h> #include <linux/io.h>
#include "../include/sysctl.h"
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
struct ctc5236_mc { struct ctc5236_mc {
struct device *dev; struct device *dev;
void __iomem *base; void __iomem *base;
int irq; int irq;
int irq1; /* one bit ecc error irq num, only use in TM1.1 */
int irq2; /* more than one bit ecc error irq num, only use in TM1.1 */
int irq_cache_ecc; /* cache error interrupt */
struct regmap *regmap_base;
unsigned int soc_ver;
}; };
/* DDR interrupt enable register */ /* DDR interrupt enable register */
#define DDR_ERR_INT_EN 0xF0 #define DDR_ERR_INT_EN 0xF0
/* DDR interrupt status register */ /* DDR interrupt status register */
#define DDR_ERR_INT_STATUS 0xF4 #define DDR_ERR_INT_STATUS 0xF4
/* over top-bound info register*/ /* over top-bound info register*/
#define DDR_ERR_INT_OVER_TOPBOUND_L 0xF8 #define DDR_ERR_INT_OVER_TOPBOUND_L 0xF8
#define DDR_ERR_INT_OVER_TOPBOUND_H 0xFC #define DDR_ERR_INT_OVER_TOPBOUND_H 0xFC
#define DDR_PORT0_ERR_INT_STATUS 0x1 #define DDR_PORT0_ERR_INT_STATUS 0x1
@ -45,15 +53,16 @@ struct ctc5236_mc {
#define DDR_PORT2_ERR_INT_STATUS 0x3 #define DDR_PORT2_ERR_INT_STATUS 0x3
#define DDR_PORT3_ERR_INT_STATUS 0x4 #define DDR_PORT3_ERR_INT_STATUS 0x4
#define DDR_ERR_ECC_INT_STATUS 0x10000 #define DDR_ERR_ECC_INT_STATUS 0x10000
#define DDR_ERR_CRC_INT_STATUS 0x200000
#define DDR_ERR_WR_PORT_REC_UNDERFLOW 0x20000 #define DDR_ERR_WR_PORT_REC_UNDERFLOW 0x20000
#define DDR_ERR_WR_PORT_REC_OVERFLOW 0x40000 #define DDR_ERR_WR_PORT_REC_OVERFLOW 0x40000
#define DDR_ERR_RD_PORT_REC_UNDERFLOW 0x80000 #define DDR_ERR_RD_PORT_REC_UNDERFLOW 0x80000
#define DDR_ERR_RD_PORT_REC_OVERFLOW 0x100000 #define DDR_ERR_RD_PORT_REC_OVERFLOW 0x100000
#define DDR_PORT0_STATUS 0xB0 #define DDR_PORT0_STATUS 0xB0
#define DDR_PORT1_STATUS 0xB4 #define DDR_PORT1_STATUS 0xB4
#define DDR_PORT2_STATUS 0xB8 #define DDR_PORT2_STATUS 0xB8
#define DDR_PORT3_STATUS 0xBC #define DDR_PORT3_STATUS 0xBC
#define DDR_ERR_OVER_TOPBOUND 0x20000 #define DDR_ERR_OVER_TOPBOUND 0x20000
#define DDR_ERR_WCMDQ_OVER 0x40000 #define DDR_ERR_WCMDQ_OVER 0x40000
@ -76,10 +85,10 @@ struct ctc5236_mc {
#define DDR_PORT2_BASE 0xB8 #define DDR_PORT2_BASE 0xB8
#define DDR_PORT3_BASE 0xBc #define DDR_PORT3_BASE 0xBc
#define DDR_PORT0 0 #define DDR_PORT0 0
#define DDR_PORT1 1 #define DDR_PORT1 1
#define DDR_PORT2 2 #define DDR_PORT2 2
#define DDR_PORT3 3 #define DDR_PORT3 3
static int port_err_status(int status, int port, void *dev_id) static int port_err_status(int status, int port, void *dev_id)
{ {
@ -98,53 +107,80 @@ static int port_err_status(int status, int port, void *dev_id)
temp = (addr_l | (((unsigned long)((addr_h >> 12) & 0x3)) << 32) temp = (addr_l | (((unsigned long)((addr_h >> 12) & 0x3)) << 32)
); );
pr_emerg("ERROR:port%d is out of top-bound range!\n" printk(KERN_EMERG
"The error address is 0x%p\n", "ERROR:port%d is out of top-bound range!\n The error address is 0x%p\n",
id, (void *)temp); id, (void *)temp);
} }
if (status & DDR_ERR_WCMDQ_OVER) if (status & DDR_ERR_WCMDQ_OVER) {
pr_err("ERROR:port%d write command queue is overflow!\n", id); printk(KERN_ERR
"ERROR:port%d write command queue is overflow!\n", id);
}
if (status & DDR_ERR_WCMDQ_UNDER) if (status & DDR_ERR_WCMDQ_UNDER) {
pr_err("ERROR:port%d write command queue is underflow!\n", id); printk(KERN_ERR
"ERROR:port%d write command queue is underflow!\n", id);
}
if (status & DDR_ERR_WDATAQ_OVER) if (status & DDR_ERR_WDATAQ_OVER) {
pr_err("ERROR:port%d write data queue is overflow!\n", id); printk(KERN_ERR "ERROR:port%d write data queue is overflow!\n",
id);
}
if (status & DDR_ERR_WDATAQ_UNDER) if (status & DDR_ERR_WDATAQ_UNDER) {
pr_err("ERROR:port%d write data queue is underflow!\n", id); printk(KERN_ERR "ERROR:port%d write data queue is underflow!\n",
id);
}
if (status & DDR_ERR_WESPQ_OVER) if (status & DDR_ERR_WESPQ_OVER) {
pr_err("ERROR:port%d write response queue is overflow!\n", id); printk(KERN_ERR
"ERROR:port%d write response queue is overflow!\n", id);
}
if (status & DDR_ERR_WESPQ_UNDER) if (status & DDR_ERR_WESPQ_UNDER) {
pr_err("ERROR:port%d write response queue is underflow!\n", id); printk(KERN_ERR
"ERROR:port%d write response queue is underflow!\n", id);
}
if (status & DDR_ERR_WINFOQ_OVER) if (status & DDR_ERR_WINFOQ_OVER) {
pr_err("ERROR:port%d write info queue is overflow!\n", id); printk(KERN_ERR "ERROR:port%d write info queue is overflow!\n",
id);
}
if (status & DDR_ERR_WINFOQ_UNDER) if (status & DDR_ERR_WINFOQ_UNDER) {
pr_err("ERROR:port%d write info queue is underflow!\n", id); printk(KERN_ERR "ERROR:port%d write info queue is underflow!\n",
id);
}
if (status & DDR_ERR_RCMDQ_OVER) if (status & DDR_ERR_RCMDQ_OVER) {
pr_err("ERROR:port%d read command queue is overflow!\n", id); printk(KERN_ERR
"ERROR:port%d read command queue is overflow!\n", id);
}
if (status & DDR_ERR_RCMDQ_UNDER) if (status & DDR_ERR_RCMDQ_UNDER) {
pr_err("ERROR:port%d read command queue is underflow!\n", id); printk(KERN_ERR
"ERROR:port%d read command queue is underflow!\n", id);
}
if (status & DDR_ERR_RDATAQ_OVER) if (status & DDR_ERR_RDATAQ_OVER) {
pr_err("ERROR:port%d read data queue is overflow!\n", id); printk(KERN_ERR "ERROR:port%d read data queue is overflow!\n",
id);
}
if (status & DDR_ERR_RDATAQ_UNDER) if (status & DDR_ERR_RDATAQ_UNDER) {
pr_err("ERROR:port%d read data queue is underflow!\n", id); printk(KERN_ERR "ERROR:port%d read data queue is underflow!\n",
id);
}
if (status & DDR_ERR_RESPQ_OVER) if (status & DDR_ERR_RESPQ_OVER) {
pr_err("ERROR:port%d read response queue is overflow!\n", id); printk(KERN_ERR
"ERROR:port%d read response queue is overflow!\n", id);
if (status & DDR_ERR_RESPQ_UNDER) }
pr_err("ERROR:port%d read response queue is underflow!\n", id);
if (status & DDR_ERR_RESPQ_UNDER) {
printk(KERN_ERR
"ERROR:port%d read response queue is underflow!\n", id);
}
return 1; return 1;
} }
@ -177,20 +213,29 @@ static irqreturn_t ctc_mc_err_handler(int irq, void *dev_id)
port_err_status(ret, DDR_PORT3, mci); port_err_status(ret, DDR_PORT3, mci);
} }
if (status & DDR_ERR_ECC_INT_STATUS) if (status & DDR_ERR_ECC_INT_STATUS) {
pr_err("ERROR:The ecc more than 1-bit error !\n"); printk(KERN_ERR "ERROR:The ecc more than 1-bit error !\n");
}
if (status & DDR_ERR_WR_PORT_REC_UNDERFLOW) if (status & DDR_ERR_WR_PORT_REC_UNDERFLOW) {
pr_err("ERROR:MPARB wr_port_rec FIFO is underflow!\n"); printk(KERN_ERR "ERROR:MPARB wr_port_rec FIFO is underflow!\n");
}
if (status & DDR_ERR_WR_PORT_REC_OVERFLOW) if (status & DDR_ERR_WR_PORT_REC_OVERFLOW) {
pr_err("ERROR:MPARB wr_port_rec FIFO is overflow!\n"); printk(KERN_ERR "ERROR:MPARB wr_port_rec FIFO is overflow!\n");
}
if (status & DDR_ERR_RD_PORT_REC_UNDERFLOW) if (status & DDR_ERR_RD_PORT_REC_UNDERFLOW) {
pr_err("ERROR:MPARB rd_port_rec FIFO is underflow!\n"); printk(KERN_ERR "ERROR:MPARB rd_port_rec FIFO is underflow!\n");
}
if (status & DDR_ERR_RD_PORT_REC_OVERFLOW) if (status & DDR_ERR_RD_PORT_REC_OVERFLOW) {
pr_err("ERROR:MPARB rd_port_rec FIFO is underflow!\n"); printk(KERN_ERR "ERROR:MPARB rd_port_rec FIFO is overflow!\n");
}
if (status & DDR_ERR_CRC_INT_STATUS) {
printk(KERN_ERR "ERROR:The crc error from DRAM!\n");
}
/* disable DDR interrupt */ /* disable DDR interrupt */
writel(0x0, mci->base + DDR_ERR_INT_EN); writel(0x0, mci->base + DDR_ERR_INT_EN);
@ -198,6 +243,43 @@ static irqreturn_t ctc_mc_err_handler(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t ctc_mc_onebit_ecc_err_handler(int irq, void *dev_id)
{
struct ctc5236_mc *mci = dev_id;
unsigned int val;
printk(KERN_ERR "ERROR:One-Bit ECC Error!\n");
regmap_read(mci->regmap_base,
offsetof(struct SysCtl_regs, SysDdrEccCtl), &val);
printk(KERN_ERR "One-Bit ECC Error Count is %d\n", ((val >> 8) & 0xf));
printk(KERN_ERR "more than One-Bit ECC Error Count is %d\n",
((val >> 12) & 0xf));
return IRQ_HANDLED;
}
static irqreturn_t ctc_mc_twobit_ecc_err_handler(int irq, void *dev_id)
{
struct ctc5236_mc *mci = dev_id;
unsigned int val;
printk(KERN_ERR "ERROR:more than One-Bit ECC Error!\n");
regmap_read(mci->regmap_base,
offsetof(struct SysCtl_regs, SysDdrEccCtl), &val);
printk(KERN_ERR "One-Bit ECC Error Count is %d\n", ((val >> 8) & 0xf));
printk(KERN_ERR "more than One-Bit ECC Error Count is %d\n",
((val >> 12) & 0xf));
return IRQ_HANDLED;
}
static irqreturn_t ctc_cache_err_handler(int irq, void *dev_id)
{
printk(KERN_ERR "ERROR:Cache ECC Error!\n");
return IRQ_HANDLED;
}
static const struct of_device_id ctc5236_ddr_ctrl_of_match[] = { static const struct of_device_id ctc5236_ddr_ctrl_of_match[] = {
{ {
.compatible = "ctc,ctc5236-ddr-ctrl", .compatible = "ctc,ctc5236-ddr-ctrl",
@ -212,6 +294,7 @@ static int ctc5236_mc_probe(struct platform_device *pdev)
const struct of_device_id *id; const struct of_device_id *id;
struct ctc5236_mc *mci; struct ctc5236_mc *mci;
int ret; int ret;
unsigned int val;
id = of_match_device(ctc5236_ddr_ctrl_of_match, &pdev->dev); id = of_match_device(ctc5236_ddr_ctrl_of_match, &pdev->dev);
if (!id) if (!id)
@ -225,12 +308,67 @@ static int ctc5236_mc_probe(struct platform_device *pdev)
if (IS_ERR(mci->base)) if (IS_ERR(mci->base))
return PTR_ERR(mci->base); return PTR_ERR(mci->base);
mci->regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(mci->regmap_base))
return PTR_ERR(mci->regmap_base);
regmap_read(mci->regmap_base,
offsetof(struct SysCtl_regs, SysCtlSysRev), &val);
mci->soc_ver = val;
mci->irq = platform_get_irq(pdev, 0); mci->irq = platform_get_irq(pdev, 0);
ret = ret =
devm_request_irq(&pdev->dev, mci->irq, ctc_mc_err_handler, 0, devm_request_irq(&pdev->dev, mci->irq, ctc_mc_err_handler, 0,
dev_name(&pdev->dev), mci); "DDR Ecc", mci);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "Unable to request irq %d\n", mci->irq); dev_err(&pdev->dev, "Unable to request ddr error irq %d\n",
mci->irq);
goto err;
}
val = readl(mci->base);
/* register ecc interrupt when use TM1.1 soc and enable ecc function */
if ((0x0 != (val & BIT(10))) && (0x1 == mci->soc_ver)) {
/* clean ecc status */
regmap_write(mci->regmap_base,
offsetof(struct SysCtl_regs, SysDdrEccCtl), 0x1);
regmap_write(mci->regmap_base,
offsetof(struct SysCtl_regs, SysDdrEccCtl), 0x0);
mci->irq1 = platform_get_irq(pdev, 1);
ret =
devm_request_irq(&pdev->dev, mci->irq1,
ctc_mc_twobit_ecc_err_handler, 0,
"DDR two-bit Ecc(TM1.1)", mci);
if (ret < 0) {
dev_err(&pdev->dev,
"Unable to request ddr two-bit ecc error irq %d\n",
mci->irq1);
goto err;
}
mci->irq2 = platform_get_irq(pdev, 2);
ret =
devm_request_irq(&pdev->dev, mci->irq2,
ctc_mc_onebit_ecc_err_handler, 0,
"DDR one-bit Ecc(TM1.1)", mci);
if (ret < 0) {
dev_err(&pdev->dev,
"Unable to request one-bit ecc error irq %d\n",
mci->irq2);
goto err;
}
}
mci->irq_cache_ecc = platform_get_irq(pdev, 3);
ret =
devm_request_irq(&pdev->dev, mci->irq_cache_ecc,
ctc_cache_err_handler, 0, "Cache Ecc", mci);
if (ret < 0) {
dev_err(&pdev->dev,
"Unable to request cache ecc error irq %d\n",
mci->irq_cache_ecc);
goto err; goto err;
} }
@ -276,4 +414,4 @@ module_exit(ctc5236_mc_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Centec Network"); MODULE_AUTHOR("Centec Network");
MODULE_DESCRIPTION("Centec TsingMa memory controller driver"); MODULE_DESCRIPTION("Centec TsingMa memory contoller driver");

View File

@ -1,4 +1,5 @@
/* (C) Copyright 2004-2017 Centec Networks (suzhou) Co., LTD. /*
* (C) Copyright 2004-2017 Centec Networks (suzhou) Co., LTD.
* Wangyb <wangyb@centecnetworks.com> * Wangyb <wangyb@centecnetworks.com>
* *
* SPDX-License-Identifier: GPL-2.0+ * SPDX-License-Identifier: GPL-2.0+
@ -9,15 +10,15 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <../include/ctc5236_switch.h> #include "../include/ctc5236_switch.h"
struct ctc_access_t *access; ctc_access_t *access;
#define SWITCH_DTS_OFFSET 0x1000 #define SWITCH_DTS_OFFSET 0x1000
int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value) int ctc5236_switch_read(u32 offset, u32 len, u32 * p_value)
{ {
union ctc_switch_cmd_status_u_t cmd_status_u; ctc_switch_cmd_status_u_t cmd_status_u;
u32 timeout = 0x6400; u32 timeout = 0x6400;
u32 cmd_len = 0; u32 cmd_len = 0;
u8 index = 0; u8 index = 0;
@ -28,8 +29,8 @@ int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
} }
/* switch only have 16 databuf, len must not exceed 16 */ /* switch only have 16 databuf, len must not exceed 16 */
if (len > 16 || len == 0) { if ((16 < len) || (0 == len)) {
pr_err("switch read: length error! len = %d\n", len); pr_err("switch read: length error! len = %d \n", len);
return -1; return -1;
} }
/* cmdDataLen must be power of 2 */ /* cmdDataLen must be power of 2 */
@ -39,22 +40,23 @@ int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
cmd_len = len; cmd_len = len;
do { do {
cmd_len++; cmd_len++;
} while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1))); }
while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
} }
/* 1. write CmdStatusReg */ /* 1. write CmdStatusReg */
memset(&cmd_status_u, 0, sizeof(union ctc_switch_cmd_status_u_t)); memset(&cmd_status_u, 0, sizeof(ctc_switch_cmd_status_u_t));
cmd_status_u.cmd_status.cmdReadType = 1; cmd_status_u.cmd_status.cmdReadType = 1;
/* normal operate only support 1 entry */ cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len; /* normal operate only support 1 entry */
cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len;
cmd_status_u.cmd_status.cmdDataLen = len; cmd_status_u.cmd_status.cmdDataLen = len;
writel(cmd_status_u.val, &access->cmd_status); writel(cmd_status_u.val, &access->cmd_status);
/* 2. write AddrReg */ /* 2. write AddrReg */
writel(offset, &access->addr); writel(offset, &access->addr);
/* 3. polling status and check */ /* 3. polling status and check */
cmd_status_u.val = readl(&access->cmd_status); cmd_status_u.val = readl(&access->cmd_status);
while (!(cmd_status_u.cmd_status.reqProcDone) && (--timeout)) while (!(cmd_status_u.cmd_status.reqProcDone) && (--timeout)) {
cmd_status_u.val = readl(&access->cmd_status); cmd_status_u.val = readl(&access->cmd_status);
}
/* 4. check cmd done */ /* 4. check cmd done */
if (!(cmd_status_u.cmd_status.reqProcDone)) { if (!(cmd_status_u.cmd_status.reqProcDone)) {
pr_err("switch read error! cmd_status = %x\n", pr_err("switch read error! cmd_status = %x\n",
@ -69,15 +71,16 @@ int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
} }
/* 6. read data from buffer */ /* 6. read data from buffer */
for (index = 0; index < len; index++) for (index = 0; index < len; index++) {
p_value[index] = readl(&access->data[index]); p_value[index] = readl(&access->data[index]);
}
return 0; return 0;
} }
int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value) int ctc5236_switch_write(u32 offset, u32 len, u32 * p_value)
{ {
union ctc_switch_cmd_status_u_t cmd_status_u; ctc_switch_cmd_status_u_t cmd_status_u;
u32 timeout = 0x6400; /* need to be confirmed */ u32 timeout = 0x6400; /* need to be confirmed */
u32 cmd_len = 0; u32 cmd_len = 0;
u8 index = 0; u8 index = 0;
@ -88,8 +91,8 @@ int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
} }
/* switch only have 16 databuf, len must not exceed 16 */ /* switch only have 16 databuf, len must not exceed 16 */
if (len > 16 || len == 0) { if ((16 < len) || (0 == len)) {
pr_err("switch write length error! len = %d\n", len); pr_err("switch write length error! len = %d \n", len);
return -1; return -1;
} }
@ -100,28 +103,28 @@ int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
cmd_len = len; cmd_len = len;
do { do {
cmd_len++; cmd_len++;
} while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1))); }
while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
} }
/* 1. write CmdStatusReg */ /* 1. write CmdStatusReg */
memset(&cmd_status_u, 0, sizeof(struct ctc_switch_cmd_status_t)); memset(&cmd_status_u, 0, sizeof(ctc_switch_cmd_status_t));
cmd_status_u.cmd_status.cmdReadType = 0; cmd_status_u.cmd_status.cmdReadType = 0;
cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len; cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len;
/* Notice: for 1 entry op, cmdDatalen eq cmdEntryWords, cmd_status_u.cmd_status.cmdDataLen = len; /* Notice: for 1 entry op, cmdDatalen eq cmdEntryWords, but for mutil entry, len = cmd_len */
* but for mutil entry, len = cmd_len
*/
cmd_status_u.cmd_status.cmdDataLen = len;
writel(cmd_status_u.val, &access->cmd_status); writel(cmd_status_u.val, &access->cmd_status);
/* 2. write AddrReg */ /* 2. write AddrReg */
writel(offset, &access->addr); writel(offset, &access->addr);
/* 3. write data into databuffer */ /* 3. write data into databuffer */
for (index = 0; index < len; index++) for (index = 0; index < len; index++) {
writel(p_value[index], &access->data[index]); writel(p_value[index], &access->data[index]);
}
/* 4. polling status and check */ /* 4. polling status and check */
cmd_status_u.val = readl(&access->cmd_status); cmd_status_u.val = readl(&access->cmd_status);
while (!(cmd_status_u.cmd_status.reqProcDone) && (--timeout)) while (!(cmd_status_u.cmd_status.reqProcDone) && (--timeout)) {
cmd_status_u.val = readl(&access->cmd_status); cmd_status_u.val = readl(&access->cmd_status);
}
/* 5. check cmd done */ /* 5. check cmd done */
if (!(cmd_status_u.cmd_status.reqProcDone)) { if (!(cmd_status_u.cmd_status.reqProcDone)) {
@ -141,29 +144,29 @@ int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
} }
static int static int
_sys_tsingma_peri_get_temp_with_code(u8 lchip, u32 temp_code, u32 *p_temp_val) _sys_tsingma_peri_get_temp_with_code(u8 lchip, u32 temp_code, u32 * p_temp_val)
{ {
u16 temp_mapping_tbl[SYS_TSINGMA_TEMP_TABLE_NUM + 1] = { u16 temp_mapping_tbl[SYS_TSINGMA_TEMP_TABLE_NUM + 1] = {
804, 801, 798, 795, 792, 790, 787, 784, 781, 778, 804, 801, 798, 795, 792, 790, 787, 784, 781, 778, 775, 772, 769, 766, 763, 761, 758, 755, 752, 749,
775, 772, 769, 766, 763, 761, 758, 755, 752, 749, /*-40~-21*/
746, 743, 740, 737, 734, 731, 728, 725, 722, 719, 746, 743, 740, 737, 734, 731, 728, 725, 722, 719, 717, 714, 711, 708, 705, 702, 699, 696, 693, 690,
717, 714, 711, 708, 705, 702, 699, 696, 693, 690, /*-20~-1*/
687, 684, 681, 678, 675, 672, 669, 666, 663, 660, 687, 684, 681, 678, 675, 672, 669, 666, 663, 660, 658, 655, 652, 649, 646, 643, 640, 637, 634, 631, /*0~19 */
658, 655, 652, 649, 646, 643, 640, 637, 634, 631, 628, 625, 622, 619, 616, 613, 610, 607, 604, 601, 599, 596, 593, 590, 587, 584, 581, 578, 575, 572, /*20~39 */
628, 625, 622, 619, 616, 613, 610, 607, 604, 601, 569, 566, 563, 560, 557, 554, 551, 548, 545, 542, 540, 537, 534, 531, 528, 525, 522, 519, 516, 513, /*40~59 */
599, 596, 593, 590, 587, 584, 581, 578, 575, 572, 510, 507, 504, 501, 498, 495, 492, 489, 486, 483, 481, 478, 475, 472, 469, 466, 463, 460, 457, 454, /*60~79 */
569, 566, 563, 560, 557, 554, 551, 548, 545, 542, 451, 448, 445, 442, 439, 436, 433, 430, 427, 424, 421, 418, 415, 412, 409, 406, 403, 400, 397, 394, /*80~99 */
540, 537, 534, 531, 528, 525, 522, 519, 516, 513, 391, 388, 385, 382, 379, 376, 373, 370, 367, 364, 361, 358, 355, 352, 349, 346, 343, 340, 337, 334, /*100~119 */
510, 507, 504, 501, 498, 495, 492, 489, 486, 483,
481, 478, 475, 472, 469, 466, 463, 460, 457, 454,
451, 448, 445, 442, 439, 436, 433, 430, 427, 424,
421, 418, 415, 412, 409, 406, 403, 400, 397, 394,
391, 388, 385, 382, 379, 376, 373, 370, 367, 364,
361, 358, 355, 352, 349, 346, 343, 340, 337, 334,
331, 328, 325, 322, 319, 316, 0 331, 328, 325, 322, 319, 316, 0
}; }; /*120~125 */
u8 index = 0; u8 index = 0;
/*if ((temp_code > temp_mapping_tbl[0]) || (temp_code < temp_mapping_tbl[SYS_TSINGMA_TEMP_TABLE_NUM-1]))
{
SYS_PERI_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "temp code error %d\n", temp_code);
return CTC_E_HW_INVALID_INDEX;
} */
for (index = 0; index < SYS_TSINGMA_TEMP_TABLE_NUM; index++) { for (index = 0; index < SYS_TSINGMA_TEMP_TABLE_NUM; index++) {
if ((temp_code <= temp_mapping_tbl[index]) if ((temp_code <= temp_mapping_tbl[index])
&& (temp_code > temp_mapping_tbl[index + 1])) { && (temp_code > temp_mapping_tbl[index + 1])) {
@ -171,10 +174,11 @@ _sys_tsingma_peri_get_temp_with_code(u8 lchip, u32 temp_code, u32 *p_temp_val)
} }
} }
if (index < 39) if (index < 40) {
*p_temp_val = 40 - index + (1 << 31); *p_temp_val = 40 - index + (1 << 31);
else } else {
*p_temp_val = index - 40; *p_temp_val = index - 40;
}
return 0; return 0;
} }
@ -189,27 +193,6 @@ int get_switch_temperature(void)
offset = 0xf * 4; offset = 0xf * 4;
ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value); ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value);
/*config RTHMC_RST=1 */
/*mask_write tbl-reg OmcMem 0x10 offset 0x0 0x00000010 0x00000010 */
offset = 0x10 * 4;
ctc5236_switch_read(OMCMEM_BASE + offset, 1, &value);
value |= BIT(4);
ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value);
/*wait RTHMC_RST=1 */
/*read tbl-reg OmcMem 0x10 offset 0x0 */
timeout = SYS_TSINGMA_SENSOR_TIMEOUT;
offset = 0x10 * 4;
while (timeout) {
timeout--;
ctc5236_switch_read(OMCMEM_BASE + offset, 1, &value);
if ((BIT(4) & value) == 0)
break;
msleep(1);
}
if (timeout == 0)
return 0xffff;
/*config ENBIAS=1£¬ENVR=1£¬ENAD=1 */ /*config ENBIAS=1£¬ENVR=1£¬ENAD=1 */
/*mask_write tbl-reg OmcMem 0x11 offset 0x0 0x02000007 0x03000007 */ /*mask_write tbl-reg OmcMem 0x11 offset 0x0 0x02000007 0x03000007 */
offset = 0x11 * 4; offset = 0x11 * 4;
@ -237,6 +220,8 @@ int get_switch_temperature(void)
value |= BIT(0); value |= BIT(0);
ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value); ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value);
msleep(1);
/*mask_write tbl-reg OmcMem 0x10 offset 0x0 0x00000001 0x00000001 */ /*mask_write tbl-reg OmcMem 0x10 offset 0x0 0x00000001 0x00000001 */
offset = 0x10 * 4; offset = 0x10 * 4;
ctc5236_switch_read(OMCMEM_BASE + offset, 1, &value); ctc5236_switch_read(OMCMEM_BASE + offset, 1, &value);
@ -262,8 +247,9 @@ int get_switch_temperature(void)
msleep(1); msleep(1);
} }
if (timeout == 0) if (0 == timeout) {
return 0xffff; return 0xffff;
}
/*mask_write tbl-reg OmcMem 0x11 offset 0x0 0x00000006 0x00000006 */ /*mask_write tbl-reg OmcMem 0x11 offset 0x0 0x00000006 0x00000006 */
offset = 0x11 * 4; offset = 0x11 * 4;
@ -284,6 +270,7 @@ int get_switch_temperature(void)
return -temperature; return -temperature;
} }
EXPORT_SYMBOL_GPL(get_switch_temperature); EXPORT_SYMBOL_GPL(get_switch_temperature);
static int ctc_switch_probe(struct platform_device *pdev) static int ctc_switch_probe(struct platform_device *pdev)
@ -291,14 +278,16 @@ static int ctc_switch_probe(struct platform_device *pdev)
struct resource *iomem; struct resource *iomem;
void __iomem *ioaddr; void __iomem *ioaddr;
resource_size_t start; resource_size_t start;
uint val;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
start = iomem->start - 0x1000; start = iomem->start - 0x1000;
ioaddr = devm_ioremap(&pdev->dev, start, resource_size(iomem)); ioaddr = devm_ioremap(&pdev->dev, start, resource_size(iomem));
if (IS_ERR(ioaddr)) if (IS_ERR(ioaddr)) {
return -1; return -1;
access = (struct ctc_access_t *) ioaddr; }
access = (ctc_access_t *) ioaddr;
return 0; return 0;
} }

View File

@ -1,3 +1,4 @@
/*
/* drivers/char/watchdog/ctc-wdt.c /* drivers/char/watchdog/ctc-wdt.c
* *
* Watchdog driver for CTC TSINGMA, based on ARM SP805 watchdog module * Watchdog driver for CTC TSINGMA, based on ARM SP805 watchdog module
@ -290,6 +291,12 @@ static int ctc_wdt_probe(struct amba_device *adev, const struct amba_id *id)
if (IS_ERR(wdt->regmap_base)) if (IS_ERR(wdt->regmap_base))
return PTR_ERR(wdt->regmap_base); return PTR_ERR(wdt->regmap_base);
/* reset wdt module */
regmap_write(wdt->regmap_base,
offsetof(struct SysCtl_regs, SysWdtResetCtl), 0x3);
regmap_write(wdt->regmap_base,
offsetof(struct SysCtl_regs, SysWdtResetCtl), 0x0);
/* /*
* TsingMa SoC wdt reference clock is obtained by clockSub frequency * TsingMa SoC wdt reference clock is obtained by clockSub frequency
* division,which is 500Mhz.So we need to set the frequency division * division,which is 500Mhz.So we need to set the frequency division

View File

@ -1,3 +1,3 @@
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/tsingma-bsp/src/ctc5236_switch/Module.symvers KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/tsingma-bsp/src/ctc5236_switch/Module.symvers
obj-m = ctcmac.o ctcmac_test.o ctc5236_mdio.o obj-m = ctcmac.o ctc5236_mdio.o

View File

@ -1,4 +1,5 @@
/* Centec cpu_mac Ethernet Driver -- cpu_mac controller implementation /*
* Centec CpuMac Ethernet Driver -- CpuMac controller implementation
* Provides Bus interface for MIIM regs * Provides Bus interface for MIIM regs
* *
* Author: liuht <liuht@centecnetworks.com> * Author: liuht <liuht@centecnetworks.com>
@ -10,6 +11,7 @@
* under the terms of the GNU General Public License as published by the * 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 * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
*
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
@ -44,7 +46,7 @@
struct ctc_mdio_priv { struct ctc_mdio_priv {
void __iomem *map; void __iomem *map;
struct mdio_soc_regs *mdio_reg; struct MdioSoc_regs *mdio_reg;
}; };
static int ctc_mdio_write(struct mii_bus *bus, int mii_id, int reg, u16 value) static int ctc_mdio_write(struct mii_bus *bus, int mii_id, int reg, u16 value)
@ -57,14 +59,15 @@ static int ctc_mdio_write(struct mii_bus *bus, int mii_id, int reg, u16 value)
cmd = CTCMAC_MDIO_CMD_REGAD(reg) | CTCMAC_MDIO_CMD_PHYAD(mii_id) cmd = CTCMAC_MDIO_CMD_REGAD(reg) | CTCMAC_MDIO_CMD_PHYAD(mii_id)
| CTCMAC_MDIO_CMD_OPCODE(1) | CTCMAC_MDIO_CMD_DATA(value); | CTCMAC_MDIO_CMD_OPCODE(1) | CTCMAC_MDIO_CMD_DATA(value);
writel(cmd, &priv->mdio_reg->mdio_soc_cmd_0[0]); writel(cmd, &priv->mdio_reg->MdioSocCmd0[0]);
writel(1, &priv->mdio_reg->mdio_soc_cmd_0[1]); writel(1, &priv->mdio_reg->MdioSocCmd0[1]);
ret = readl_poll_timeout(&priv->mdio_reg->mdio_soc_status_0, ret = readl_poll_timeout(&priv->mdio_reg->MdioSocStatus0,
tmp, tmp & CTCMAC_MDIO_STAT(1), 1000, 10000); tmp, tmp & CTCMAC_MDIO_STAT(1), 1000, 10000);
if (ret < 0) if (ret < 0) {
return -1; return -1;
}
return 0; return 0;
} }
@ -80,18 +83,18 @@ static int ctc_mdio_read(struct mii_bus *bus, int mii_id, int reg)
cmd = CTCMAC_MDIO_CMD_REGAD(reg) | CTCMAC_MDIO_CMD_PHYAD(mii_id) cmd = CTCMAC_MDIO_CMD_REGAD(reg) | CTCMAC_MDIO_CMD_PHYAD(mii_id)
| CTCMAC_MDIO_CMD_OPCODE(2); | CTCMAC_MDIO_CMD_OPCODE(2);
writel(cmd, &priv->mdio_reg->mdio_soc_cmd_0[0]); writel(cmd, &priv->mdio_reg->MdioSocCmd0[0]);
writel(1, &priv->mdio_reg->mdio_soc_cmd_0[1]); writel(1, &priv->mdio_reg->MdioSocCmd0[1]);
ret = readl_poll_timeout(&priv->mdio_reg->mdio_soc_status_0, ret = readl_poll_timeout(&priv->mdio_reg->MdioSocStatus0,
status, status & CTCMAC_MDIO_STAT(1), 1000, status, status & CTCMAC_MDIO_STAT(1), 1000,
10000); 10000);
if (ret < 0) { if (ret < 0) {
pr_err("ctc_mdio_read1\n"); printk(KERN_ERR "ctc_mdio_read1\n");
return -1; return -1;
} }
value = (readl(&priv->mdio_reg->mdio_soc_status_0) & 0xffff); value = (readl(&priv->mdio_reg->MdioSocStatus0) & 0xffff);
return value; return value;
} }
@ -100,7 +103,7 @@ static int ctc_mdio_reset(struct mii_bus *bus)
{ {
struct ctc_mdio_priv *priv = (struct ctc_mdio_priv *)bus->priv; struct ctc_mdio_priv *priv = (struct ctc_mdio_priv *)bus->priv;
writel(0x91f, &priv->mdio_reg->mdio_soc_cfg_0); writel(0x91f, &priv->mdio_reg->MdioSocCfg0);
return 0; return 0;
} }
@ -146,7 +149,7 @@ static int ctc_mdio_probe(struct platform_device *pdev)
pr_err("of iomap fail %d!\n", err); pr_err("of iomap fail %d!\n", err);
goto error; goto error;
} }
priv->mdio_reg = (struct mdio_soc_regs *)priv->map; priv->mdio_reg = (struct MdioSoc_regs *)priv->map;
new_bus->parent = &pdev->dev; new_bus->parent = &pdev->dev;
platform_set_drvdata(pdev, new_bus); platform_set_drvdata(pdev, new_bus);

2156
platform/centec-arm64/tsingma-bsp/src/ctcmac/ctcmac.c Normal file → Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/* /*
* Centec cpu_mac Ethernet Driver -- cpu_mac controller implementation * Centec CpuMac Ethernet Driver -- CpuMac controller implementation
* Provides Bus interface for MIIM regs * Provides Bus interface for MIIM regs
* *
* Author: liuht <liuht@centecnetworks.com> * Author: liuht <liuht@centecnetworks.com>
@ -17,7 +17,7 @@
#ifndef __CTCMAC_H #ifndef __CTCMAC_H
#define __CTCMAC_H #define __CTCMAC_H
#define TX_TIMEOUT (5 * HZ) #define TX_TIMEOUT (5*HZ)
#define CTCMAC_DEFAULT_MTU 1500 #define CTCMAC_DEFAULT_MTU 1500
#define CTCMAC_MIN_PKT_LEN 64 #define CTCMAC_MIN_PKT_LEN 64
@ -47,14 +47,18 @@
#define CTCMAC_TOKEN_PER_PKT 10 #define CTCMAC_TOKEN_PER_PKT 10
#define CTCMAC_TIMER_COMPENSATE 1 #define CTCMAC_TIMER_COMPENSATE 1
#define CTCMAC_NOR_RX1_R BIT(7) #define CTCMAC_NOR_RX1_R (1<<7)
#define CTCMAC_NOR_RX0_R BIT(6) #define CTCMAC_NOR_RX0_R (1<<6)
#define CTCMAC_NOR_RX1_D BIT(5) #define CTCMAC_NOR_RX1_D (1<<5)
#define CTCMAC_NOR_RX0_D BIT(4) #define CTCMAC_NOR_RX0_D (1<<4)
#define CTCMAC_NOR_TX_D BIT(3) #define CTCMAC_NOR_TX_D (1<<3)
#define CTCMAC_NOR_AN_D BIT(2) #define CTCMAC_NOR_AN_D (1<<2)
#define CTCMAC_NOR_LINK_DOWN BIT(1) #define CTCMAC_NOR_LINK_DOWN (1<<1)
#define CTCMAC_NOR_LINK_UP BIT(0) #define CTCMAC_NOR_LINK_UP (1<<0)
#define CTCMAC_FUNC0_RX_D (1<<0)
#define CTCMAC_FUNC0_RX_R (1<<1)
#define CTCMAC_FUNC1_RX_D (1<<0)
#define CTCMAC_FUNC1_RX_R (1<<1)
#define CTC_DDR_BASE 0x80000000 #define CTC_DDR_BASE 0x80000000
@ -72,25 +76,33 @@
#define CSC_100M 0x02400000 #define CSC_100M 0x02400000
#define CSC_10M 0x18c00000 #define CSC_10M 0x18c00000
#define CTCMAC_DESC_INT_NUM 1 #define DESC_INT_COALESCE_CNT_MIN 1
#define DESC_TX_INT_COALESCE_CNT_DEFAULT 16
#define DESC_RX_INT_COALESCE_CNT_DEFAULT 16
/* emu 100us */
//#define CTCMAC_TIMER_THRD 0x4B0
/* board 100us */
#define CTCMAC_TIMER_THRD 0xc350
#define CTCMAC_SUPPORTED (SUPPORTED_10baseT_Full \ #define CTCMAC_SUPPORTED (SUPPORTED_10baseT_Full \
| SUPPORTED_100baseT_Full \ | SUPPORTED_100baseT_Full \
| SUPPORTED_1000baseT_Full \ | SUPPORTED_1000baseT_Full \
| SUPPORTED_Autoneg) | SUPPORTED_Autoneg)
#define CTCMAC_STATS_LEN (sizeof(struct ctcmac_pkt_stats) / sizeof(u64)) #define CTCMAC_STATS_LEN (sizeof(struct ctcmac_pkt_stats)/sizeof(u64))
struct ctcmac_skb_cb { struct ctcmac_skb_cb {
unsigned int bytes_sent; /* bytes-on-wire (i.e. no FCB) */ unsigned int bytes_sent; /* bytes-on-wire (i.e. no FCB) */
}; };
#define CTCMAC_CB(skb) ((struct ctcmac_skb_cb *)((skb)->cb)) #define CTCMAC_CB(skb) ((struct ctcmac_skb_cb *)((skb)->cb))
enum ctcmac_irqinfo_id { enum ctcmac_irqinfo_id {
CTCMAC_NORMAL = 0, CTCMAC_NORMAL = 0,
CTCMAC_FUNC, CTCMAC_FUNC,
CTCMAC_UNIT, CTCMAC_UNIT,
CTCMAC_FUNC_RX0,
CTCMAC_FUNC_RX1,
CTCMAC_NUM_IRQS CTCMAC_NUM_IRQS
}; };
@ -105,6 +117,16 @@ enum ctcmac_int_type {
CTCMAC_INT_MAX CTCMAC_INT_MAX
}; };
enum ctcmac_tx_pol_inv {
CTCMAC_TX_POL_INV_DISABLE,
CTCMAC_TX_POL_INV_ENABLE,
};
enum ctcmac_rx_pol_inv {
CTCMAC_RX_POL_INV_DISABLE,
CTCMAC_RX_POL_INV_ENABLE,
};
enum ctcmac_autoneg { enum ctcmac_autoneg {
CTCMAC_AUTONEG_1000BASEX_M, CTCMAC_AUTONEG_1000BASEX_M,
CTCMAC_AUTONEG_PHY_M, CTCMAC_AUTONEG_PHY_M,
@ -113,34 +135,46 @@ enum ctcmac_autoneg {
CTCMAC_AUTONEG_MAX CTCMAC_AUTONEG_MAX
}; };
/* Per TX queue stats */ /*
* Per TX queue stats
*/
struct txq_stats { struct txq_stats {
unsigned long tx_packets; unsigned long tx_packets;
unsigned long tx_bytes; unsigned long tx_bytes;
}; };
struct tx_skb {
struct sk_buff *skb;
int frag_merge;
};
struct ctcmac_tx_buff { struct ctcmac_tx_buff {
void *vaddr; void *vaddr;
dma_addr_t dma; dma_addr_t dma;
u32 len; u32 len;
u32 offset; u32 offset;
u8 alloc; bool alloc;
}; };
struct ctcmac_priv_tx_q { struct ctcmac_priv_tx_q {
spinlock_t txlock __aligned(SMP_CACHE_BYTES); spinlock_t txlock __attribute__ ((aligned(SMP_CACHE_BYTES)));
struct ctcmac_tx_buff tx_buff[CTCMAC_MAX_RING_SIZE + 1]; struct ctcmac_tx_buff tx_buff[CTCMAC_MAX_RING_SIZE + 1];
unsigned int num_txbdfree; unsigned int num_txbdfree;
u16 tx_ring_size; u16 tx_ring_size;
u16 qindex; u16 qindex;
u16 next_to_alloc; u16 skb_cur;
u16 next_to_clean; u16 skb_dirty;
u16 desc_cur;
u16 desc_dirty;
struct txq_stats stats; struct txq_stats stats;
struct net_device *dev; struct net_device *dev;
struct sk_buff **tx_skbuff; struct tx_skb *tx_skbuff;
struct napi_struct napi_tx;
}; };
/*Per RX queue stats */ /*
* Per RX queue stats
*/
struct rxq_stats { struct rxq_stats {
unsigned long rx_packets; unsigned long rx_packets;
unsigned long rx_bytes; unsigned long rx_bytes;
@ -167,6 +201,7 @@ struct ctcmac_priv_rx_q {
u32 pps_limit; u32 pps_limit;
u32 token, token_max; u32 token, token_max;
u32 rx_trigger; u32 rx_trigger;
struct napi_struct napi_rx;
}; };
struct ctcmac_irqinfo { struct ctcmac_irqinfo {
@ -189,9 +224,9 @@ struct ctcmac_private {
struct device *dev; struct device *dev;
struct net_device *ndev; struct net_device *ndev;
void __iomem *iobase; void __iomem *iobase;
struct cpu_mac_regs __iomem *cpumac_reg; struct CpuMac_regs __iomem *cpumac_reg;
struct cpu_mac_mems __iomem *cpumac_mem; struct CpuMac_mems __iomem *cpumac_mem;
struct cpu_mac_unit_regs *cpumacu_reg; struct CpuMacUnit_regs *cpumacu_reg;
u32 device_flags; u32 device_flags;
int irq_num; int irq_num;
int index; int index;
@ -213,9 +248,10 @@ struct ctcmac_private {
struct work_struct reset_task; struct work_struct reset_task;
struct platform_device *ofdev; struct platform_device *ofdev;
struct napi_struct napi_rx;
struct napi_struct napi_tx;
struct ctcmac_irqinfo irqinfo[CTCMAC_NUM_IRQS]; struct ctcmac_irqinfo irqinfo[CTCMAC_NUM_IRQS];
struct napi_struct napi_tx;
struct napi_struct napi_rx;
struct napi_struct napi_rx1;
int hwts_rx_en; int hwts_rx_en;
int hwts_tx_en; int hwts_tx_en;
@ -223,8 +259,16 @@ struct ctcmac_private {
u32 supported; u32 supported;
u32 msg_enable; u32 msg_enable;
u32 int_type; u32 int_type;
u32 rx_int_coalesce_cnt;
u32 tx_int_coalesce_cnt;
u8 dfe_enable; u8 dfe_enable;
u8 tx_pol_inv;
u8 rx_pol_inv;
struct timer_list token_timer; struct timer_list token_timer;
u8 version;
u8 pause_aneg_en;
u8 tx_pause_en;
u8 rx_pause_en;
}; };
struct ctcmac_pkt_stats { struct ctcmac_pkt_stats {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

266
platform/centec-arm64/tsingma-bsp/src/ehci-ctc/ehci-ctc.c Normal file → Executable file
View File

@ -14,13 +14,18 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/sys_soc.h>
#include <linux/timer.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/usb/hcd.h> #include <linux/usb/hcd.h>
#include <linux/usb/ehci_pdriver.h> #include <linux/usb/ehci_pdriver.h>
#include <linux/usb/of.h>
#include "../include/sysctl.h"
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include "ehci.h" #include "ehci.h"
#define DRIVER_DESC "Centec EHCI platform driver" #define DRIVER_DESC "Centec EHCI platform driver"
@ -28,12 +33,15 @@
#define EHCI_MAX_RSTS 4 #define EHCI_MAX_RSTS 4
#define hcd_to_ehci_priv(h) ((struct ehci_ctc_priv *)hcd_to_ehci(h)->priv) #define hcd_to_ehci_priv(h) ((struct ehci_ctc_priv *)hcd_to_ehci(h)->priv)
static struct regmap *regmap_base;
struct ehci_ctc_priv { struct ehci_ctc_priv {
struct clk *clks[EHCI_MAX_CLKS]; struct clk *clks[EHCI_MAX_CLKS];
struct reset_control *rsts[EHCI_MAX_RSTS]; struct reset_control *rsts;
struct phy **phys;
int num_phys;
bool reset_on_resume; bool reset_on_resume;
bool quirk_poll;
struct timer_list poll_timer;
struct delayed_work poll_work;
}; };
static const char hcd_name[] = "ehci-ctc"; static const char hcd_name[] = "ehci-ctc";
@ -67,7 +75,7 @@ static int ehci_ctc_power_on(struct platform_device *dev)
{ {
struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd); struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
int clk, ret, phy_num; int clk, ret;
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
ret = clk_prepare_enable(priv->clks[clk]); ret = clk_prepare_enable(priv->clks[clk]);
@ -75,24 +83,8 @@ static int ehci_ctc_power_on(struct platform_device *dev)
goto err_disable_clks; goto err_disable_clks;
} }
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
ret = phy_init(priv->phys[phy_num]);
if (ret)
goto err_exit_phy;
ret = phy_power_on(priv->phys[phy_num]);
if (ret) {
phy_exit(priv->phys[phy_num]);
goto err_exit_phy;
}
}
return 0; return 0;
err_exit_phy:
while (--phy_num >= 0) {
phy_power_off(priv->phys[phy_num]);
phy_exit(priv->phys[phy_num]);
}
err_disable_clks: err_disable_clks:
while (--clk >= 0) while (--clk >= 0)
clk_disable_unprepare(priv->clks[clk]); clk_disable_unprepare(priv->clks[clk]);
@ -104,12 +96,7 @@ static void ehci_ctc_power_off(struct platform_device *dev)
{ {
struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_hcd *hcd = platform_get_drvdata(dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd); struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
int clk, phy_num; int clk;
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
phy_power_off(priv->phys[phy_num]);
phy_exit(priv->phys[phy_num]);
}
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
if (priv->clks[clk]) if (priv->clks[clk])
@ -124,22 +111,146 @@ static const struct ehci_driver_overrides platform_overrides __initconst = {
}; };
static struct usb_ehci_pdata ehci_ctc_defaults = { static struct usb_ehci_pdata ehci_ctc_defaults = {
.caps_offset = 0x100,
.dma_mask_64 = 1,
.power_on = ehci_ctc_power_on, .power_on = ehci_ctc_power_on,
.power_suspend = ehci_ctc_power_off, .power_suspend = ehci_ctc_power_off,
.power_off = ehci_ctc_power_off, .power_off = ehci_ctc_power_off,
}; };
#define KERN_CTC KERN_ERR #define KERN_CTC KERN_ERR
static bool quirk_poll_check_port_status(struct ehci_hcd *ehci)
{
u32 port_status = ehci_readl(ehci, &ehci->regs->port_status[0]);
if (!(port_status & PORT_OWNER) &&
(port_status & PORT_POWER) &&
!(port_status & PORT_CONNECT) && (port_status & PORT_LS_MASK))
return true;
return false;
}
/**
* quirk_poll_rebind_companion - rebind comanion device to recover
* @ehci: the ehci hcd pointer
*
* Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting
* stuck very rarely after a full/low usb device was disconnected. To
* recover from such a situation, the controllers require changing the OHCI
* functional state.
*/
static void quirk_poll_rebind_companion(struct ehci_hcd *ehci)
{
struct device *companion_dev;
struct usb_hcd *hcd = ehci_to_hcd(ehci);
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
if (!companion_dev)
return;
device_release_driver(companion_dev);
if (device_attach(companion_dev) < 0)
ehci_err(ehci, "%s: failed\n", __func__);
put_device(companion_dev);
}
static void quirk_poll_work(struct work_struct *work)
{
struct ehci_ctc_priv *priv =
container_of(to_delayed_work(work), struct ehci_ctc_priv,
poll_work);
struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd,
priv);
/* check the status twice to reduce misdetection rate */
if (!quirk_poll_check_port_status(ehci))
return;
udelay(10);
if (!quirk_poll_check_port_status(ehci))
return;
ehci_dbg(ehci, "%s: detected getting stuck. rebind now!\n", __func__);
quirk_poll_rebind_companion(ehci);
}
static void quirk_poll_timer(struct timer_list *t)
{
struct ehci_ctc_priv *priv = from_timer(priv, t, poll_timer);
struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd,
priv);
if (quirk_poll_check_port_status(ehci)) {
/*
* Now scheduling the work for testing the port more. Note that
* updating the status is possible to be delayed when
* reconnection. So, this uses delayed work with 5 ms delay
* to avoid misdetection.
*/
schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(5));
}
mod_timer(&priv->poll_timer, jiffies + HZ);
}
static void quirk_poll_init(struct ehci_ctc_priv *priv)
{
INIT_DELAYED_WORK(&priv->poll_work, quirk_poll_work);
timer_setup(&priv->poll_timer, quirk_poll_timer, 0);
mod_timer(&priv->poll_timer, jiffies + HZ);
}
static void quirk_poll_end(struct ehci_ctc_priv *priv)
{
del_timer_sync(&priv->poll_timer);
cancel_delayed_work(&priv->poll_work);
}
static const struct soc_device_attribute quirk_poll_match[] = {
{.family = "R-Car Gen3"},
{ /* sentinel */ }
};
static int ehci_ctc_probe(struct platform_device *dev) static int ehci_ctc_probe(struct platform_device *dev)
{ {
u32 val;
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct resource *res_mem; struct resource *res_mem;
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ehci_ctc_priv *priv; struct ehci_ctc_priv *priv;
struct ehci_hcd *ehci; struct ehci_hcd *ehci;
int err, irq, phy_num, clk = 0, rst; int err, irq, clk = 0;
regmap_base =
syscon_regmap_lookup_by_phandle(dev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(regmap_base))
return PTR_ERR(regmap_base);
/* USB interface reset config */
val = 0x7f;
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
udelay(1);
val &= ~SYS_USB_RESET_CTL_W0_CFG_USB_PHY_PWR_ON_RESET;
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
udelay(1);
val &=
~(SYS_USB_RESET_CTL_W0_CFG_USB_PHY_RESET |
SYS_USB_RESET_CTL_W0_CFG_USB_PHY_PORT_RESET |
SYS_USB_RESET_CTL_W0_CFG_USB_UTMI_RESET);
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
udelay(1);
val &=
~(SYS_USB_RESET_CTL_W0_CFG_USB_INTF_RESET |
SYS_USB_RESET_CTL_W0_CFG_USB_AUX_RESET);
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
udelay(1);
val &= ~SYS_USB_RESET_CTL_W0_CFG_USB_PHY_ATE_RESET;
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysUsbResetCtl),
val);
mdelay(500);
if (usb_disabled()) if (usb_disabled())
return -ENODEV; return -ENODEV;
@ -152,18 +263,17 @@ static int ehci_ctc_probe(struct platform_device *dev)
pdata = &ehci_ctc_defaults; pdata = &ehci_ctc_defaults;
err = dma_coerce_mask_and_coherent(&dev->dev, err = dma_coerce_mask_and_coherent(&dev->dev,
pdata->dma_mask_64 ? pdata->
DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); dma_mask_64 ? DMA_BIT_MASK(64) :
DMA_BIT_MASK(32));
if (err) { if (err) {
dev_err(&dev->dev, "Error: DMA mask configuration failed\n"); dev_err(&dev->dev, "Error: DMA mask configuration failed\n");
return err; return err;
} }
irq = platform_get_irq(dev, 0); irq = platform_get_irq(dev, 0);
if (irq < 0) { if (irq < 0)
dev_err(&dev->dev, "no irq provided");
return irq; return irq;
}
hcd = usb_create_hcd(&ehci_ctc_hc_driver, &dev->dev, hcd = usb_create_hcd(&ehci_ctc_hc_driver, &dev->dev,
dev_name(&dev->dev)); dev_name(&dev->dev));
@ -174,6 +284,7 @@ static int ehci_ctc_probe(struct platform_device *dev)
dev->dev.platform_data = pdata; dev->dev.platform_data = pdata;
priv = hcd_to_ehci_priv(hcd); priv = hcd_to_ehci_priv(hcd);
ehci = hcd_to_ehci(hcd); ehci = hcd_to_ehci(hcd);
if (pdata == &ehci_ctc_defaults && dev->dev.of_node) { if (pdata == &ehci_ctc_defaults && dev->dev.of_node) {
if (of_property_read_bool(dev->dev.of_node, "big-endian-regs")) if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
ehci->big_endian_mmio = 1; ehci->big_endian_mmio = 1;
@ -192,28 +303,8 @@ static int ehci_ctc_probe(struct platform_device *dev)
"has-transaction-translator")) "has-transaction-translator"))
hcd->has_tt = 1; hcd->has_tt = 1;
priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, if (soc_device_match(quirk_poll_match))
"phys", priv->quirk_poll = true;
"#phy-cells");
if (priv->num_phys > 0) {
priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
sizeof(struct phy *),
GFP_KERNEL);
if (!priv->phys)
return -ENOMEM;
} else
priv->num_phys = 0;
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
priv->phys[phy_num] =
devm_of_phy_get_by_index(&dev->dev,
dev->dev.of_node, phy_num);
if (IS_ERR(priv->phys[phy_num])) {
err = PTR_ERR(priv->phys[phy_num]);
goto err_put_hcd;
}
}
for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
@ -226,21 +317,17 @@ static int ehci_ctc_probe(struct platform_device *dev)
} }
} }
} }
for (rst = 0; rst < EHCI_MAX_RSTS; rst++) {
priv->rsts[rst] =
devm_reset_control_get_shared_by_index(&dev->dev, rst);
if (IS_ERR(priv->rsts[rst])) {
err = PTR_ERR(priv->rsts[rst]);
if (err == -EPROBE_DEFER)
goto err_reset;
priv->rsts[rst] = NULL;
break;
}
err = reset_control_deassert(priv->rsts[rst]); priv->rsts = devm_reset_control_array_get_optional_shared(&dev->dev);
if (err) if (IS_ERR(priv->rsts)) {
goto err_reset; err = PTR_ERR(priv->rsts);
goto err_put_clks;
} }
err = reset_control_deassert(priv->rsts);
if (err)
goto err_put_clks;
if (pdata->big_endian_desc) if (pdata->big_endian_desc)
ehci->big_endian_desc = 1; ehci->big_endian_desc = 1;
if (pdata->big_endian_mmio) if (pdata->big_endian_mmio)
@ -272,6 +359,7 @@ static int ehci_ctc_probe(struct platform_device *dev)
if (err < 0) if (err < 0)
goto err_reset; goto err_reset;
} }
res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0); res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
if (IS_ERR(hcd->regs)) { if (IS_ERR(hcd->regs)) {
@ -280,23 +368,29 @@ static int ehci_ctc_probe(struct platform_device *dev)
} }
hcd->rsrc_start = res_mem->start; hcd->rsrc_start = res_mem->start;
hcd->rsrc_len = resource_size(res_mem); hcd->rsrc_len = resource_size(res_mem);
err = usb_add_hcd(hcd, irq, IRQF_SHARED); err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err) if (err)
goto err_power; goto err_power;
device_wakeup_enable(hcd->self.controller); device_wakeup_enable(hcd->self.controller);
device_enable_async_suspend(hcd->self.controller);
platform_set_drvdata(dev, hcd); platform_set_drvdata(dev, hcd);
if (priv->quirk_poll)
quirk_poll_init(priv);
return err; return err;
err_power: err_power:
if (pdata->power_off) if (pdata->power_off)
pdata->power_off(dev); pdata->power_off(dev);
err_reset: err_reset:
while (--rst >= 0) reset_control_assert(priv->rsts);
reset_control_assert(priv->rsts[rst]);
err_put_clks: err_put_clks:
while (--clk >= 0) while (--clk >= 0)
clk_put(priv->clks[clk]); clk_put(priv->clks[clk]);
err_put_hcd:
if (pdata == &ehci_ctc_defaults) if (pdata == &ehci_ctc_defaults)
dev->dev.platform_data = NULL; dev->dev.platform_data = NULL;
@ -310,15 +404,17 @@ static int ehci_ctc_remove(struct platform_device *dev)
struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_hcd *hcd = platform_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd); struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
int clk, rst; int clk;
if (priv->quirk_poll)
quirk_poll_end(priv);
usb_remove_hcd(hcd); usb_remove_hcd(hcd);
if (pdata->power_off) if (pdata->power_off)
pdata->power_off(dev); pdata->power_off(dev);
for (rst = 0; rst < EHCI_MAX_RSTS && priv->rsts[rst]; rst++) reset_control_assert(priv->rsts);
reset_control_assert(priv->rsts[rst]);
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++)
clk_put(priv->clks[clk]); clk_put(priv->clks[clk]);
@ -337,9 +433,13 @@ static int ehci_ctc_suspend(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_ehci_pdata *pdata = dev_get_platdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
bool do_wakeup = device_may_wakeup(dev); bool do_wakeup = device_may_wakeup(dev);
int ret; int ret;
if (priv->quirk_poll)
quirk_poll_end(priv);
ret = ehci_suspend(hcd, do_wakeup); ret = ehci_suspend(hcd, do_wakeup);
if (ret) if (ret)
return ret; return ret;
@ -356,15 +456,25 @@ static int ehci_ctc_resume(struct device *dev)
struct usb_ehci_pdata *pdata = dev_get_platdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd); struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
struct device *companion_dev;
if (pdata->power_on) { if (pdata->power_on) {
int err = pdata->power_on(pdev); int err = pdata->power_on(pdev);
if (err < 0) if (err < 0)
return err; return err;
} }
companion_dev = usb_of_get_companion_dev(hcd->self.controller);
if (companion_dev) {
device_pm_wait_for_dev(hcd->self.controller, companion_dev);
put_device(companion_dev);
}
ehci_resume(hcd, priv->reset_on_resume); ehci_resume(hcd, priv->reset_on_resume);
if (priv->quirk_poll)
quirk_poll_init(priv);
return 0; return 0;
} }
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
@ -394,7 +504,7 @@ static struct platform_driver ehci_ctc_driver = {
.name = "ehci-ctc", .name = "ehci-ctc",
.pm = &ehci_ctc_pm_ops, .pm = &ehci_ctc_pm_ops,
.of_match_table = ctc_ehci_ids, .of_match_table = ctc_ehci_ids,
} }
}; };
static int __init ehci_ctc_init(void) static int __init ehci_ctc_init(void)

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+ /* SPDX-License-Identifier: GPL-2.0+ */
/* /*
* Copyright (c) 2001-2002 by David Brownell * Copyright (c) 2001-2002 by David Brownell
*/ */
@ -31,14 +31,14 @@ typedef __u16 __bitwise __hc16;
struct ehci_stats { struct ehci_stats {
/* irq usage */ /* irq usage */
unsigned long normal; unsigned long normal;
unsigned long error; unsigned long error;
unsigned long iaa; unsigned long iaa;
unsigned long lost_iaa; unsigned long lost_iaa;
/* termination of urbs from core */ /* termination of urbs from core */
unsigned long complete; unsigned long complete;
unsigned long unlink; unsigned long unlink;
}; };
/* /*
@ -46,22 +46,22 @@ struct ehci_stats {
* high-speed devices and full/low-speed devices lying behind a TT. * high-speed devices and full/low-speed devices lying behind a TT.
*/ */
struct ehci_per_sched { struct ehci_per_sched {
struct usb_device *udev; /* access to the TT */ struct usb_device *udev; /* access to the TT */
struct usb_host_endpoint *ep; struct usb_host_endpoint *ep;
struct list_head ps_list; /* node on ehci_tt's ps_list */ struct list_head ps_list; /* node on ehci_tt's ps_list */
u16 tt_usecs; /* time on the FS/LS bus */ u16 tt_usecs; /* time on the FS/LS bus */
u16 cs_mask; /* C-mask and S-mask bytes */ u16 cs_mask; /* C-mask and S-mask bytes */
u16 period; /* actual period in frames */ u16 period; /* actual period in frames */
u16 phase; /* actual phase, frame part */ u16 phase; /* actual phase, frame part */
u8 bw_phase; /* same, for bandwidth u8 bw_phase; /* same, for bandwidth
reservation */ reservation */
u8 phase_uf; /* uframe part of the phase */ u8 phase_uf; /* uframe part of the phase */
u8 usecs, c_usecs; /* times on the HS bus */ u8 usecs, c_usecs; /* times on the HS bus */
u8 bw_uperiod; /* period in microframes, for u8 bw_uperiod; /* period in microframes, for
bandwidth reservation */ bandwidth reservation */
u8 bw_period; /* same, in frames */ u8 bw_period; /* same, in frames */
}; };
#define NO_FRAME 29999 /* frame not assigned yet */ #define NO_FRAME 29999 /* frame not assigned yet */
/* ehci_hcd->lock guards shared data against other CPUs: /* ehci_hcd->lock guards shared data against other CPUs:
* ehci_hcd: async, unlink, periodic (and shadow), ... * ehci_hcd: async, unlink, periodic (and shadow), ...
@ -73,7 +73,7 @@ struct ehci_per_sched {
* when updating hw_* fields in shared qh/qtd/... structures. * when updating hw_* fields in shared qh/qtd/... structures.
*/ */
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ #define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
/* /*
* ehci_rh_state values of EHCI_RH_RUNNING or above mean that the * ehci_rh_state values of EHCI_RH_RUNNING or above mean that the
@ -92,180 +92,180 @@ enum ehci_rh_state {
* ehci-timer.c) in parallel with this list. * ehci-timer.c) in parallel with this list.
*/ */
enum ehci_hrtimer_event { enum ehci_hrtimer_event {
EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */ EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */
EHCI_HRTIMER_ACTIVE_UNLINK, /* Wait while unlinking an active QH */ EHCI_HRTIMER_ACTIVE_UNLINK, /* Wait while unlinking an active QH */
EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */ EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */
EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */
EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */
EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
EHCI_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ EHCI_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */
EHCI_HRTIMER_NUM_EVENTS /* Must come last */ EHCI_HRTIMER_NUM_EVENTS /* Must come last */
}; };
#define EHCI_HRTIMER_NO_EVENT 99 #define EHCI_HRTIMER_NO_EVENT 99
struct ehci_hcd { /* one per controller */ struct ehci_hcd { /* one per controller */
/* timing support */ /* timing support */
enum ehci_hrtimer_event next_hrtimer_event; enum ehci_hrtimer_event next_hrtimer_event;
unsigned enabled_hrtimer_events; unsigned enabled_hrtimer_events;
ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS]; ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
struct hrtimer hrtimer; struct hrtimer hrtimer;
int PSS_poll_count; int PSS_poll_count;
int ASS_poll_count; int ASS_poll_count;
int died_poll_count; int died_poll_count;
/* glue to PCI and HCD framework */ /* glue to PCI and HCD framework */
struct ehci_caps __iomem *caps; struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs; struct ehci_regs __iomem *regs;
struct ehci_dbg_port __iomem *debug; struct ehci_dbg_port __iomem *debug;
__u32 hcs_params; /* cached register copy */ __u32 hcs_params; /* cached register copy */
spinlock_t lock; spinlock_t lock;
enum ehci_rh_state rh_state; enum ehci_rh_state rh_state;
/* general schedule support */ /* general schedule support */
bool scanning:1; bool scanning:1;
bool need_rescan:1; bool need_rescan:1;
bool intr_unlinking:1; bool intr_unlinking:1;
bool iaa_in_progress:1; bool iaa_in_progress:1;
bool async_unlinking:1; bool async_unlinking:1;
bool shutdown:1; bool shutdown:1;
struct ehci_qh *qh_scan_next; struct ehci_qh *qh_scan_next;
/* async schedule support */ /* async schedule support */
struct ehci_qh *async; struct ehci_qh *async;
struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *dummy; /* For AMD quirk use */
struct list_head async_unlink; struct list_head async_unlink;
struct list_head async_idle; struct list_head async_idle;
unsigned async_unlink_cycle; unsigned async_unlink_cycle;
unsigned async_count; /* async activity count */ unsigned async_count; /* async activity count */
__hc32 old_current; /* Test for QH becoming */ __hc32 old_current; /* Test for QH becoming */
__hc32 old_token; /* inactive during unlink */ __hc32 old_token; /* inactive during unlink */
/* periodic schedule support */ /* periodic schedule support */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */
unsigned periodic_size; unsigned periodic_size;
__hc32 *periodic; /* hw periodic table */ __hc32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma; dma_addr_t periodic_dma;
struct list_head intr_qh_list; struct list_head intr_qh_list;
unsigned i_thresh; /* uframes HC might cache */ unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */
struct list_head intr_unlink_wait;
struct list_head intr_unlink;
unsigned intr_unlink_wait_cycle;
unsigned intr_unlink_cycle;
unsigned now_frame; /* frame from HC hardware */
unsigned last_iso_frame; /* last frame scanned for iso */
unsigned intr_count; /* intr activity count */
unsigned isoc_count; /* isoc activity count */
unsigned periodic_count; /* periodic activity count */
unsigned uframe_periodic_max; /* max periodic time per uframe */
union ehci_shadow *pshadow; /* mirror hw periodic table */
struct list_head intr_unlink_wait;
struct list_head intr_unlink;
unsigned intr_unlink_wait_cycle;
unsigned intr_unlink_cycle;
unsigned now_frame; /* frame from HC hardware */
unsigned last_iso_frame; /* last frame scanned for iso */
unsigned intr_count; /* intr activity count */
unsigned isoc_count; /* isoc activity count */
unsigned periodic_count; /* periodic activity count */
unsigned uframe_periodic_max; /* max periodic time per uframe */
/* list of itds & sitds completed while now_frame was still active */ /* list of itds & sitds completed while now_frame was still active */
struct list_head cached_itd_list; struct list_head cached_itd_list;
struct ehci_itd *last_itd_to_free; struct ehci_itd *last_itd_to_free;
struct list_head cached_sitd_list; struct list_head cached_sitd_list;
struct ehci_sitd *last_sitd_to_free; struct ehci_sitd *last_sitd_to_free;
/* per root hub port */ /* per root hub port */
unsigned long reset_done[EHCI_MAX_ROOT_PORTS]; unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
/* bit vectors (one bit per port) */ /* bit vectors (one bit per port) */
unsigned long bus_suspended; /* which ports were unsigned long bus_suspended; /* which ports were
already suspended at the start of a bus suspend */ already suspended at the start of a bus suspend */
unsigned long companion_ports; /* which ports are unsigned long companion_ports; /* which ports are
dedicated to the companion controller */ dedicated to the companion controller */
unsigned long owned_ports; /* which ports are unsigned long owned_ports; /* which ports are
owned by the companion during a bus suspend */ owned by the companion during a bus suspend */
unsigned long port_c_suspend; /* which ports have unsigned long port_c_suspend; /* which ports have
the change-suspend feature turned on */ the change-suspend feature turned on */
unsigned long suspended_ports; /* which ports are unsigned long suspended_ports; /* which ports are
suspended */ suspended */
unsigned long resuming_ports; /* which ports have unsigned long resuming_ports; /* which ports have
started to resume */ started to resume */
/* per-HC memory pools (could be per-bus, but ...) */ /* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */ struct dma_pool *qh_pool; /* qh per active urb */
struct dma_pool *qtd_pool; /* one or more per qh */ struct dma_pool *qtd_pool; /* one or more per qh */
struct dma_pool *itd_pool; /* itd per iso urb */ struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */ struct dma_pool *sitd_pool; /* sitd per split iso urb */
unsigned random_frame; unsigned random_frame;
unsigned long next_statechange; unsigned long next_statechange;
ktime_t last_periodic_enable; ktime_t last_periodic_enable;
u32 command; u32 command;
/* SILICON QUIRKS */ /* SILICON QUIRKS */
unsigned no_selective_suspend:1; unsigned no_selective_suspend:1;
unsigned has_fsl_port_bug:1; /* FreeScale */ unsigned has_fsl_port_bug:1; /* FreeScale */
unsigned has_fsl_hs_errata:1; /* Freescale HS quirk */ unsigned has_fsl_hs_errata:1; /* Freescale HS quirk */
unsigned has_fsl_susp_errata:1; /* NXP SUSP quirk */ unsigned has_fsl_susp_errata:1; /* NXP SUSP quirk */
unsigned big_endian_mmio:1; unsigned big_endian_mmio:1;
unsigned big_endian_desc:1; unsigned big_endian_desc:1;
unsigned big_endian_capbase:1; unsigned big_endian_capbase:1;
unsigned has_amcc_usb23:1; unsigned has_amcc_usb23:1;
unsigned need_io_watchdog:1; unsigned need_io_watchdog:1;
unsigned amd_pll_fix:1; unsigned amd_pll_fix:1;
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ unsigned use_dummy_qh:1; /* AMD Frame List table quirk */
unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
unsigned need_oc_pp_cycle:1; /* MPC834X port power */ unsigned need_oc_pp_cycle:1; /* MPC834X port power */
unsigned imx28_write_fix:1; /* For Freescale i.MX28 */ unsigned imx28_write_fix:1; /* For Freescale i.MX28 */
/* required for usb32 quirk */ /* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6) #define OHCI_CTRL_HCFS (3 << 6)
#define OHCI_USB_OPER (2 << 6) #define OHCI_USB_OPER (2 << 6)
#define OHCI_USB_SUSPEND (3 << 6) #define OHCI_USB_SUSPEND (3 << 6)
#define OHCI_HCCTRL_OFFSET 0x4 #define OHCI_HCCTRL_OFFSET 0x4
#define OHCI_HCCTRL_LEN 0x4 #define OHCI_HCCTRL_LEN 0x4
__hc32 *ohci_hcctrl_reg; __hc32 *ohci_hcctrl_reg;
unsigned has_hostpc:1; unsigned has_hostpc:1;
unsigned has_tdi_phy_lpm:1; unsigned has_tdi_phy_lpm:1;
unsigned has_ppcd:1; /* support per-port change bits */ unsigned has_ppcd:1; /* support per-port change bits */
u8 sbrn; /* packed release number */ u8 sbrn; /* packed release number */
/* irq statistics */ /* irq statistics */
#ifdef EHCI_STATS #ifdef EHCI_STATS
struct ehci_stats stats; struct ehci_stats stats;
# define COUNT(x) ((x)++) # define INCR(x) ((x)++)
#else #else
# define COUNT(x) # define INCR(x) do {} while (0)
#endif #endif
/* debug files */ /* debug files */
#ifdef CONFIG_DYNAMIC_DEBUG #ifdef CONFIG_DYNAMIC_DEBUG
struct dentry *debug_dir; struct dentry *debug_dir;
#endif #endif
/* bandwidth usage */ /* bandwidth usage */
#define EHCI_BANDWIDTH_SIZE 64 #define EHCI_BANDWIDTH_SIZE 64
#define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3) #define EHCI_BANDWIDTH_FRAMES (EHCI_BANDWIDTH_SIZE >> 3)
u8 bandwidth[EHCI_BANDWIDTH_SIZE]; u8 bandwidth[EHCI_BANDWIDTH_SIZE];
/* us allocated per uframe */ /* us allocated per uframe */
u8 tt_budget[EHCI_BANDWIDTH_SIZE]; u8 tt_budget[EHCI_BANDWIDTH_SIZE];
/* us budgeted per uframe */ /* us budgeted per uframe */
struct list_head tt_list; struct list_head tt_list;
/* platform-specific data -- must come last */ /* platform-specific data -- must come last */
unsigned long priv[0] __aligned(sizeof(s64)); unsigned long priv[] __aligned(sizeof(s64));
}; };
/* convert between an HCD pointer and the corresponding EHCI_HCD */ /* convert between an HCD pointer and the corresponding EHCI_HCD */
static inline struct ehci_hcd *hcd_to_ehci(struct usb_hcd *hcd) static inline struct ehci_hcd *hcd_to_ehci(struct usb_hcd *hcd)
{ {
return (struct ehci_hcd *) (hcd->hcd_priv); return (struct ehci_hcd *)(hcd->hcd_priv);
} }
static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci) static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)
{ {
return container_of((void *) ehci, struct usb_hcd, hcd_priv); return container_of((void *)ehci, struct usb_hcd, hcd_priv);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -286,9 +286,9 @@ static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)
*/ */
struct ehci_qtd { struct ehci_qtd {
/* first part defined by EHCI spec */ /* first part defined by EHCI spec */
__hc32 hw_next; /* see EHCI 3.5.1 */ __hc32 hw_next; /* see EHCI 3.5.1 */
__hc32 hw_alt_next; /* see EHCI 3.5.2 */ __hc32 hw_alt_next; /* see EHCI 3.5.2 */
__hc32 hw_token; /* see EHCI 3.5.3 */ __hc32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */ #define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) #define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */ #define QTD_IOC (1 << 15) /* interrupt on complete */
@ -307,14 +307,14 @@ struct ehci_qtd {
#define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT) #define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT)
#define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS) #define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS)
__hc32 hw_buf[5]; /* see EHCI 3.5.4 */ __hc32 hw_buf[5]; /* see EHCI 3.5.4 */
__hc32 hw_buf_hi[5]; /* Appendix B */ __hc32 hw_buf_hi[5]; /* Appendix B */
/* the rest is HCD-private */ /* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */ dma_addr_t qtd_dma; /* qtd address */
struct list_head qtd_list; /* sw qtd list */ struct list_head qtd_list; /* sw qtd list */
struct urb *urb; /* qtd's urb */ struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */ size_t length; /* length of buffer */
} __aligned(32); } __aligned(32);
/* mask NakCnt+T in qh->hw_alt_next */ /* mask NakCnt+T in qh->hw_alt_next */
@ -345,7 +345,7 @@ struct ehci_qtd {
(cpu_to_hc32(ehci, (((u32) dma) & ~0x01f) | Q_TYPE_QH)) (cpu_to_hc32(ehci, (((u32) dma) & ~0x01f) | Q_TYPE_QH))
/* for periodic/async schedules and qtd lists, mark end of list */ /* for periodic/async schedules and qtd lists, mark end of list */
#define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */ #define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */
/* /*
* Entries in periodic shadow table are pointers to one of four kinds * Entries in periodic shadow table are pointers to one of four kinds
@ -356,12 +356,12 @@ struct ehci_qtd {
* For entries in the async schedule, the type tag always says "qh". * For entries in the async schedule, the type tag always says "qh".
*/ */
union ehci_shadow { union ehci_shadow {
struct ehci_qh *qh; /* Q_TYPE_QH */ struct ehci_qh *qh; /* Q_TYPE_QH */
struct ehci_itd *itd; /* Q_TYPE_ITD */ struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */ struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */ struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
__hc32 *hw_next; /* (all types) */ __hc32 *hw_next; /* (all types) */
void *ptr; void *ptr;
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -376,8 +376,8 @@ union ehci_shadow {
/* first part defined by EHCI spec */ /* first part defined by EHCI spec */
struct ehci_qh_hw { struct ehci_qh_hw {
__hc32 hw_next; /* see EHCI 3.6.1 */ __hc32 hw_next; /* see EHCI 3.6.1 */
__hc32 hw_info1; /* see EHCI 3.6.2 */ __hc32 hw_info1; /* see EHCI 3.6.2 */
#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ #define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */
#define QH_HEAD (1 << 15) /* Head of async reclamation list */ #define QH_HEAD (1 << 15) /* Head of async reclamation list */
#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ #define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */
@ -385,59 +385,59 @@ struct ehci_qh_hw {
#define QH_LOW_SPEED (1 << 12) #define QH_LOW_SPEED (1 << 12)
#define QH_FULL_SPEED (0 << 12) #define QH_FULL_SPEED (0 << 12)
#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ #define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */
__hc32 hw_info2; /* see EHCI 3.6.2 */ __hc32 hw_info2; /* see EHCI 3.6.2 */
#define QH_SMASK 0x000000ff #define QH_SMASK 0x000000ff
#define QH_CMASK 0x0000ff00 #define QH_CMASK 0x0000ff00
#define QH_HUBADDR 0x007f0000 #define QH_HUBADDR 0x007f0000
#define QH_HUBPORT 0x3f800000 #define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000 #define QH_MULT 0xc0000000
__hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
/* qtd overlay (hardware parts of a struct ehci_qtd) */ /* qtd overlay (hardware parts of a struct ehci_qtd) */
__hc32 hw_qtd_next; __hc32 hw_qtd_next;
__hc32 hw_alt_next; __hc32 hw_alt_next;
__hc32 hw_token; __hc32 hw_token;
__hc32 hw_buf[5]; __hc32 hw_buf[5];
__hc32 hw_buf_hi[5]; __hc32 hw_buf_hi[5];
} __aligned(32); } __aligned(32);
struct ehci_qh { struct ehci_qh {
struct ehci_qh_hw *hw; /* Must come first */ struct ehci_qh_hw *hw; /* Must come first */
/* the rest is HCD-private */ /* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */ dma_addr_t qh_dma; /* address of qh */
union ehci_shadow qh_next; /* ptr to qh; or periodic */ union ehci_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */ struct list_head qtd_list; /* sw qtd list */
struct list_head intr_node; /* list of intr QHs */ struct list_head intr_node; /* list of intr QHs */
struct ehci_qtd *dummy; struct ehci_qtd *dummy;
struct list_head unlink_node; struct list_head unlink_node;
struct ehci_per_sched ps; /* scheduling info */ struct ehci_per_sched ps; /* scheduling info */
unsigned unlink_cycle; unsigned unlink_cycle;
u8 qh_state; u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ #define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ #define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
u8 xacterrs; /* XactErr retry counter */ u8 xacterrs; /* XactErr retry counter */
#define QH_XACTERR_MAX 32 /* XactErr retry limit */ #define QH_XACTERR_MAX 32 /* XactErr retry limit */
u8 unlink_reason; u8 unlink_reason;
#define QH_UNLINK_HALTED 0x01 /* Halt flag is set */ #define QH_UNLINK_HALTED 0x01 /* Halt flag is set */
#define QH_UNLINK_SHORT_READ 0x02 /* Recover from a short read */ #define QH_UNLINK_SHORT_READ 0x02 /* Recover from a short read */
#define QH_UNLINK_DUMMY_OVERLAY 0x04 /* QH overlayed the dummy TD */ #define QH_UNLINK_DUMMY_OVERLAY 0x04 /* QH overlayed the dummy TD */
#define QH_UNLINK_SHUTDOWN 0x08 /* The HC isn't running */ #define QH_UNLINK_SHUTDOWN 0x08 /* The HC isn't running */
#define QH_UNLINK_QUEUE_EMPTY 0x10 /* Reached end of the queue */ #define QH_UNLINK_QUEUE_EMPTY 0x10 /* Reached end of the queue */
#define QH_UNLINK_REQUESTED 0x20 /* Disable, reset, or dequeue */ #define QH_UNLINK_REQUESTED 0x20 /* Disable, reset, or dequeue */
u8 gap_uf; /* uframes split/csplit gap */ u8 gap_uf; /* uframes split/csplit gap */
unsigned is_out:1; /* bulk or intr OUT */ unsigned is_out:1; /* bulk or intr OUT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
unsigned dequeue_during_giveback:1; unsigned dequeue_during_giveback:1;
unsigned should_be_inactive:1; unsigned should_be_inactive:1;
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -445,11 +445,11 @@ struct ehci_qh {
/* description of one iso transaction (up to 3 KB data if highspeed) */ /* description of one iso transaction (up to 3 KB data if highspeed) */
struct ehci_iso_packet { struct ehci_iso_packet {
/* These will be copied to iTD when scheduling */ /* These will be copied to iTD when scheduling */
u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */
__hc32 transaction; /* itd->hw_transaction[i] |= */ __hc32 transaction; /* itd->hw_transaction[i] |= */
u8 cross; /* buf crosses pages */ u8 cross; /* buf crosses pages */
/* for full speed OUT splits */ /* for full speed OUT splits */
u32 buf1; u32 buf1;
}; };
/* temporary schedule data for packets from iso urbs (both speeds) /* temporary schedule data for packets from iso urbs (both speeds)
@ -457,10 +457,10 @@ struct ehci_iso_packet {
* beginning at stream->next_uframe * beginning at stream->next_uframe
*/ */
struct ehci_iso_sched { struct ehci_iso_sched {
struct list_head td_list; struct list_head td_list;
unsigned span; unsigned span;
unsigned first_packet; unsigned first_packet;
struct ehci_iso_packet packet[0]; struct ehci_iso_packet packet[];
}; };
/* /*
@ -469,32 +469,32 @@ struct ehci_iso_sched {
*/ */
struct ehci_iso_stream { struct ehci_iso_stream {
/* first field matches ehci_hq, but is NULL */ /* first field matches ehci_hq, but is NULL */
struct ehci_qh_hw *hw; struct ehci_qh_hw *hw;
u8 bEndpointAddress; u8 bEndpointAddress;
u8 highspeed; u8 highspeed;
struct list_head td_list; /* queued itds/sitds */ struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */ struct list_head free_list; /* list of unused itds/sitds */
/* output of (re)scheduling */ /* output of (re)scheduling */
struct ehci_per_sched ps; /* scheduling info */ struct ehci_per_sched ps; /* scheduling info */
unsigned next_uframe; unsigned next_uframe;
__hc32 splits; __hc32 splits;
/* the rest is derived from the endpoint descriptor, /* the rest is derived from the endpoint descriptor,
* including the extra info for hw_bufp[0..2] * including the extra info for hw_bufp[0..2]
*/ */
u16 uperiod; /* period in uframes */ u16 uperiod; /* period in uframes */
u16 maxp; u16 maxp;
unsigned bandwidth; unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */ /* This is used to initialize iTD's hw_bufp fields */
__hc32 buf0; __hc32 buf0;
__hc32 buf1; __hc32 buf1;
__hc32 buf2; __hc32 buf2;
/* this is used to initialize sITD's tt info */ /* this is used to initialize sITD's tt info */
__hc32 address; __hc32 address;
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -507,32 +507,32 @@ struct ehci_iso_stream {
*/ */
struct ehci_itd { struct ehci_itd {
/* first part defined by EHCI spec */ /* first part defined by EHCI spec */
__hc32 hw_next; /* see EHCI 3.3.1 */ __hc32 hw_next; /* see EHCI 3.3.1 */
__hc32 hw_transaction[8]; /* see EHCI 3.3.2 */ __hc32 hw_transaction[8]; /* see EHCI 3.3.2 */
#define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ #define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */ #define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */ #define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ #define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) #define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */ #define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE) #define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE)
__hc32 hw_bufp[7]; /* see EHCI 3.3.3 */ __hc32 hw_bufp[7]; /* see EHCI 3.3.3 */
__hc32 hw_bufp_hi[7]; /* Appendix B */ __hc32 hw_bufp_hi[7]; /* Appendix B */
/* the rest is HCD-private */ /* the rest is HCD-private */
dma_addr_t itd_dma; /* for this itd */ dma_addr_t itd_dma; /* for this itd */
union ehci_shadow itd_next; /* ptr to periodic q entry */ union ehci_shadow itd_next; /* ptr to periodic q entry */
struct urb *urb; struct urb *urb;
struct ehci_iso_stream *stream; /* endpoint's queue */ struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head itd_list; /* list of stream's itds */ struct list_head itd_list; /* list of stream's itds */
/* any/all hw_transactions here may be used by that urb */ /* any/all hw_transactions here may be used by that urb */
unsigned frame; /* where scheduled */ unsigned frame; /* where scheduled */
unsigned pg; unsigned pg;
unsigned index[8]; /* in urb->iso_frame_desc */ unsigned index[8]; /* in urb->iso_frame_desc */
} __aligned(32); } __aligned(32);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -545,11 +545,11 @@ struct ehci_itd {
*/ */
struct ehci_sitd { struct ehci_sitd {
/* first part defined by EHCI spec */ /* first part defined by EHCI spec */
__hc32 hw_next; __hc32 hw_next;
/* uses bit field macros above - see EHCI 0.95 Table 3-8 */ /* uses bit field macros above - see EHCI 0.95 Table 3-8 */
__hc32 hw_fullspeed_ep; /* EHCI table 3-9 */ __hc32 hw_fullspeed_ep; /* EHCI table 3-9 */
__hc32 hw_uframe; /* EHCI table 3-10 */ __hc32 hw_uframe; /* EHCI table 3-10 */
__hc32 hw_results; /* EHCI table 3-11 */ __hc32 hw_results; /* EHCI table 3-11 */
#define SITD_IOC (1 << 31) /* interrupt on completion */ #define SITD_IOC (1 << 31) /* interrupt on completion */
#define SITD_PAGE (1 << 30) /* buffer 0/1 */ #define SITD_PAGE (1 << 30) /* buffer 0/1 */
#define SITD_LENGTH(x) (((x) >> 16) & 0x3ff) #define SITD_LENGTH(x) (((x) >> 16) & 0x3ff)
@ -563,19 +563,19 @@ struct ehci_sitd {
#define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE) #define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE)
__hc32 hw_buf[2]; /* EHCI table 3-12 */ __hc32 hw_buf[2]; /* EHCI table 3-12 */
__hc32 hw_backpointer; /* EHCI table 3-13 */ __hc32 hw_backpointer; /* EHCI table 3-13 */
__hc32 hw_buf_hi[2]; /* Appendix B */ __hc32 hw_buf_hi[2]; /* Appendix B */
/* the rest is HCD-private */ /* the rest is HCD-private */
dma_addr_t sitd_dma; dma_addr_t sitd_dma;
union ehci_shadow sitd_next; /* ptr to periodic q entry */ union ehci_shadow sitd_next; /* ptr to periodic q entry */
struct urb *urb; struct urb *urb;
struct ehci_iso_stream *stream; /* endpoint's queue */ struct ehci_iso_stream *stream; /* endpoint's queue */
struct list_head sitd_list; /* list of stream's sitds */ struct list_head sitd_list; /* list of stream's sitds */
unsigned frame; unsigned frame;
unsigned index; unsigned index;
} __aligned(32); } __aligned(32);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -590,12 +590,12 @@ struct ehci_sitd {
* it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work.
*/ */
struct ehci_fstn { struct ehci_fstn {
__hc32 hw_next; /* any periodic q entry */ __hc32 hw_next; /* any periodic q entry */
__hc32 hw_prev; /* qh or EHCI_LIST_END */ __hc32 hw_prev; /* qh or EHCI_LIST_END */
/* the rest is HCD-private */ /* the rest is HCD-private */
dma_addr_t fstn_dma; dma_addr_t fstn_dma;
union ehci_shadow fstn_next; /* ptr to periodic q entry */ union ehci_shadow fstn_next; /* ptr to periodic q entry */
} __aligned(32); } __aligned(32);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -619,12 +619,12 @@ struct ehci_fstn {
*/ */
struct ehci_tt { struct ehci_tt {
u16 bandwidth[EHCI_BANDWIDTH_FRAMES]; u16 bandwidth[EHCI_BANDWIDTH_FRAMES];
struct list_head tt_list; /* List of all ehci_tt's */ struct list_head tt_list; /* List of all ehci_tt's */
struct list_head ps_list; /* Items using this TT */ struct list_head ps_list; /* Items using this TT */
struct usb_tt *usb_tt; struct usb_tt *usb_tt;
int tt_port; /* TT port number */ int tt_port; /* TT port number */
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -736,12 +736,10 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
#endif #endif
static inline unsigned int ehci_readl(const struct ehci_hcd *ehci, static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
__u32 __iomem *regs) __u32 __iomem * regs)
{ {
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
return ehci_big_endian_mmio(ehci) ? return ehci_big_endian_mmio(ehci) ? readl_be(regs) : readl(regs);
readl_be(regs) :
readl(regs);
#else #else
return readl(regs); return readl(regs);
#endif #endif
@ -749,23 +747,21 @@ static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
#ifdef CONFIG_SOC_IMX28 #ifdef CONFIG_SOC_IMX28
static inline void imx28_ehci_writel(const unsigned int val, static inline void imx28_ehci_writel(const unsigned int val,
volatile __u32 __iomem *addr) volatile __u32 __iomem * addr)
{ {
__asm__ ("swp %0, %0, [%1]" : : "r"(val), "r"(addr)); __asm__("swp %0, %0, [%1]": :"r"(val), "r"(addr));
} }
#else #else
static inline void imx28_ehci_writel(const unsigned int val, static inline void imx28_ehci_writel(const unsigned int val,
volatile __u32 __iomem *addr) volatile __u32 __iomem * addr)
{ {
} }
#endif #endif
static inline void ehci_writel(const struct ehci_hcd *ehci, static inline void ehci_writel(const struct ehci_hcd *ehci,
const unsigned int val, __u32 __iomem *regs) const unsigned int val, __u32 __iomem * regs)
{ {
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
ehci_big_endian_mmio(ehci) ? ehci_big_endian_mmio(ehci) ? writel_be(val, regs) : writel(val, regs);
writel_be(val, regs) :
writel(val, regs);
#else #else
if (ehci->imx28_write_fix) if (ehci->imx28_write_fix)
imx28_ehci_writel(val, regs); imx28_ehci_writel(val, regs);
@ -791,11 +787,12 @@ static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
hc_control |= OHCI_USB_SUSPEND; hc_control |= OHCI_USB_SUSPEND;
writel_be(hc_control, ehci->ohci_hcctrl_reg); writel_be(hc_control, ehci->ohci_hcctrl_reg);
(void) readl_be(ehci->ohci_hcctrl_reg); (void)readl_be(ehci->ohci_hcctrl_reg);
} }
#else #else
static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational) static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
{ } {
}
#endif #endif
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -814,23 +811,23 @@ static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
static inline __hc32 cpu_to_hc32(const struct ehci_hcd *ehci, const u32 x) static inline __hc32 cpu_to_hc32(const struct ehci_hcd *ehci, const u32 x)
{ {
return ehci_big_endian_desc(ehci) return ehci_big_endian_desc(ehci)
? (__force __hc32)cpu_to_be32(x) ? (__force __hc32) cpu_to_be32(x)
: (__force __hc32)cpu_to_le32(x); : (__force __hc32) cpu_to_le32(x);
} }
/* ehci to cpu */ /* ehci to cpu */
static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x) static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x)
{ {
return ehci_big_endian_desc(ehci) return ehci_big_endian_desc(ehci)
? be32_to_cpu((__force __be32)x) ? be32_to_cpu((__force __be32) x)
: le32_to_cpu((__force __le32)x); : le32_to_cpu((__force __le32) x);
} }
static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x) static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 * x)
{ {
return ehci_big_endian_desc(ehci) return ehci_big_endian_desc(ehci)
? be32_to_cpup((__force __be32 *)x) ? be32_to_cpup((__force __be32 *) x)
: le32_to_cpup((__force __le32 *)x); : le32_to_cpup((__force __le32 *) x);
} }
#else #else
@ -847,7 +844,7 @@ static inline u32 hc32_to_cpu(const struct ehci_hcd *ehci, const __hc32 x)
return le32_to_cpu(x); return le32_to_cpu(x);
} }
static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x) static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 * x)
{ {
return le32_to_cpup(x); return le32_to_cpup(x);
} }
@ -870,25 +867,24 @@ static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x)
/* Declarations of things exported for use by ehci platform drivers */ /* Declarations of things exported for use by ehci platform drivers */
struct ehci_driver_overrides { struct ehci_driver_overrides {
size_t extra_priv_size; size_t extra_priv_size;
int (*reset)(struct usb_hcd *hcd); int (*reset) (struct usb_hcd * hcd);
int (*port_power)(struct usb_hcd *hcd, int (*port_power) (struct usb_hcd * hcd, int portnum, bool enable);
int portnum, bool enable);
}; };
extern void ehci_init_driver(struct hc_driver *drv, extern void ehci_init_driver(struct hc_driver *drv,
const struct ehci_driver_overrides *over); const struct ehci_driver_overrides *over);
extern int ehci_setup(struct usb_hcd *hcd); extern int ehci_setup(struct usb_hcd *hcd);
extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr, extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem * ptr,
u32 mask, u32 done, int usec); u32 mask, u32 done, int usec);
extern int ehci_reset(struct ehci_hcd *ehci); extern int ehci_reset(struct ehci_hcd *ehci);
extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup); extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
extern int ehci_resume(struct usb_hcd *hcd, bool force_reset); extern int ehci_resume(struct usb_hcd *hcd, bool force_reset);
extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
bool suspending, bool do_wakeup); bool suspending, bool do_wakeup);
extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength); u16 wIndex, char *buf, u16 wLength);
#endif /* __LINUX_EHCI_HCD_H */ #endif /* __LINUX_EHCI_HCD_H */

View File

@ -25,12 +25,19 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include "gpio-ctcapb.h" #include "gpio-ctcapb.h"
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/delay.h>
#include "gpiolib.h" #include "gpiolib.h"
#include "../include/sysctl.h"
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include "gpiolib-acpi.h"
#define DWAPB_MAX_PORTS 2 #define DWAPB_MAX_PORTS 2
struct ctcapb_gpio; struct ctcapb_gpio;
static u32 soc_v;
struct ctcapb_gpio_port { struct ctcapb_gpio_port {
bool is_registered; bool is_registered;
unsigned int idx; unsigned int idx;
@ -45,14 +52,15 @@ struct ctcapb_gpio {
unsigned int nr_ports; unsigned int nr_ports;
struct GpioSoc_regs *regs; struct GpioSoc_regs *regs;
struct ctcapb_gpio_port *ports; struct ctcapb_gpio_port *ports;
struct regmap *regmap_base;
}; };
static void clrsetbits(unsigned __iomem *addr, u32 clr, u32 set) static void clrsetbits(unsigned __iomem * addr, u32 clr, u32 set)
{ {
writel((readl(addr) & ~(clr)) | (set), addr); writel((readl(addr) & ~(clr)) | (set), addr);
} }
static int ctcapb_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) static int ctcapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{ {
struct ctcapb_gpio_port *port = gpiochip_get_data(gc); struct ctcapb_gpio_port *port = gpiochip_get_data(gc);
@ -123,6 +131,20 @@ static void ctcapb_irq_unmask(struct irq_data *d)
ctcapb_irq_enable(d); ctcapb_irq_enable(d);
} }
#if 0
static void ctcapb_irq_disable(struct irq_data *d)
{
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
struct ctcapb_gpio_port *port = igc->private;
struct gpio_chip *gc = &port->gc;
unsigned long flags;
spin_lock_irqsave(&gc->bgpio_lock, flags);
clrsetbits(&port->regs->GpioIntrEn, ~BIT(d->hwirq), 0);
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
}
#endif
static int ctcapb_irq_reqres(struct irq_data *d) static int ctcapb_irq_reqres(struct irq_data *d)
{ {
struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d); struct irq_chip_generic *igc = irq_data_get_irq_chip_data(d);
@ -152,7 +174,7 @@ static int ctcapb_irq_set_type(struct irq_data *d, u32 type)
struct ctcapb_gpio_port *port = igc->private; struct ctcapb_gpio_port *port = igc->private;
struct gpio_chip *gc = &port->gc; struct gpio_chip *gc = &port->gc;
int bit = d->hwirq; int bit = d->hwirq;
unsigned long level, polarity, flags; unsigned long level, polarity, flags, datactl, outctl;
if (type & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | if (type & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING |
IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
@ -162,6 +184,21 @@ static int ctcapb_irq_set_type(struct irq_data *d, u32 type)
level = readl(&port->regs->GpioIntrLevel); level = readl(&port->regs->GpioIntrLevel);
polarity = readl(&port->regs->GpioIntrPolarity); polarity = readl(&port->regs->GpioIntrPolarity);
if (!soc_v) {
datactl = readl(&port->regs->GpioDataCtl);
outctl = readl(&port->regs->GpioOutCtl);
datactl &= ~BIT(bit);
outctl |= BIT(bit);
writel(datactl, &port->regs->GpioDataCtl);
writel(outctl, &port->regs->GpioOutCtl);
udelay(10);
outctl &= ~BIT(bit);
writel(outctl, &port->regs->GpioOutCtl);
}
switch (type) { switch (type) {
case IRQ_TYPE_EDGE_BOTH: case IRQ_TYPE_EDGE_BOTH:
level &= ~BIT(bit); level &= ~BIT(bit);
@ -195,7 +232,7 @@ static int ctcapb_irq_set_type(struct irq_data *d, u32 type)
} }
static int ctcapb_gpio_set_debounce(struct gpio_chip *gc, static int ctcapb_gpio_set_debounce(struct gpio_chip *gc,
unsigned int offset, unsigned int debounce) unsigned offset, unsigned debounce)
{ {
struct ctcapb_gpio_port *port = gpiochip_get_data(gc); struct ctcapb_gpio_port *port = gpiochip_get_data(gc);
unsigned long flags, val_deb; unsigned long flags, val_deb;
@ -445,7 +482,8 @@ static struct ctcapb_platform_data *ctcapb_gpio_get_pdata(struct device *dev)
} }
if (dev->of_node && fwnode_property_read_bool(fwnode, if (dev->of_node && fwnode_property_read_bool(fwnode,
"interrupt-controller")) { "interrupt-controller"))
{
pp->irq = irq_of_parse_and_map(to_of_node(fwnode), 0); pp->irq = irq_of_parse_and_map(to_of_node(fwnode), 0);
if (!pp->irq) if (!pp->irq)
dev_warn(dev, "no irq for port%d\n", pp->idx); dev_warn(dev, "no irq for port%d\n", pp->idx);
@ -461,12 +499,42 @@ static struct ctcapb_platform_data *ctcapb_gpio_get_pdata(struct device *dev)
return pdata; return pdata;
} }
int ctc_bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{
unsigned long mask;
if (gc->be_bits)
mask = BIT(gc->bgpio_bits - 1 - gpio);
else
mask = BIT(gpio);
if (soc_v) {
return 0;
} else {
if (val)
gc->bgpio_data |= mask;
else
gc->bgpio_data &= ~mask;
gc->write_reg(gc->reg_set, gc->bgpio_data);
if (gc->be_bits)
gc->bgpio_dir |= BIT(gc->bgpio_bits - 1 - gpio);
else
gc->bgpio_dir |= BIT(gpio);
gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
return 0;
}
}
static int ctcapb_gpio_probe(struct platform_device *pdev) static int ctcapb_gpio_probe(struct platform_device *pdev)
{ {
unsigned int i; unsigned int i;
struct resource *res; struct resource *res;
struct ctcapb_gpio *gpio; struct ctcapb_gpio *gpio;
int err; int err, val;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ctcapb_platform_data *pdata = dev_get_platdata(dev); struct ctcapb_platform_data *pdata = dev_get_platdata(dev);
@ -491,6 +559,18 @@ static int ctcapb_gpio_probe(struct platform_device *pdev)
if (!gpio->ports) if (!gpio->ports)
return -ENOMEM; return -ENOMEM;
gpio->regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(gpio->regmap_base))
return PTR_ERR(gpio->regmap_base);
regmap_read(gpio->regmap_base,
offsetof(struct SysCtl_regs, SysCtlSysRev), &val);
soc_v = val;
printk("soc version = 0x%x\n", soc_v);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio->regs = gpio->regs =
(struct GpioSoc_regs *)devm_ioremap_resource(&pdev->dev, res); (struct GpioSoc_regs *)devm_ioremap_resource(&pdev->dev, res);
@ -508,8 +588,9 @@ static int ctcapb_gpio_probe(struct platform_device *pdev)
out_unregister: out_unregister:
ctcapb_gpio_unregister(gpio); ctcapb_gpio_unregister(gpio);
for (i = 0; i < gpio->nr_ports; i++) for (i = 0; i < gpio->nr_ports; i++) {
ctcapb_irq_teardown(&gpio->ports[i]); ctcapb_irq_teardown(&gpio->ports[i]);
}
return err; return err;
} }
@ -520,8 +601,9 @@ static int ctcapb_gpio_remove(struct platform_device *pdev)
struct ctcapb_gpio *gpio = platform_get_drvdata(pdev); struct ctcapb_gpio *gpio = platform_get_drvdata(pdev);
ctcapb_gpio_unregister(gpio); ctcapb_gpio_unregister(gpio);
for (i = 0; i < gpio->nr_ports; i++) for (i = 0; i < gpio->nr_ports; i++) {
ctcapb_irq_teardown(&gpio->ports[i]); ctcapb_irq_teardown(&gpio->ports[i]);
}
return 0; return 0;
} }

View File

@ -1,27 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* /*
* Internal GPIO functions. * Internal GPIO functions.
* *
* Copyright (C) 2013, Intel Corporation * Copyright (C) 2013, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.com> * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#ifndef GPIOLIB_H #ifndef GPIOLIB_H
#define GPIOLIB_H #define GPIOLIB_H
#include <linux/gpio/driver.h> #include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */ #include <linux/gpio/consumer.h> /* for enum gpiod_flags */
#include <linux/err.h> #include <linux/err.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cdev.h> #include <linux/cdev.h>
enum of_gpio_flags; #define GPIOCHIP_NAME "gpiochip"
enum gpio_lookup_flags;
struct acpi_device;
/** /**
* struct gpio_device - internal state container for GPIO devices * struct gpio_device - internal state container for GPIO devices
@ -49,18 +44,19 @@ struct acpi_device;
* userspace. * userspace.
*/ */
struct gpio_device { struct gpio_device {
int id; int id;
struct device dev; struct device dev;
struct cdev chrdev; struct cdev chrdev;
struct device *mockdev; struct device *mockdev;
struct module *owner; struct module *owner;
struct gpio_chip *chip; struct gpio_chip *chip;
struct gpio_desc *descs; struct gpio_desc *descs;
int base; int base;
u16 ngpio; u16 ngpio;
const char *label; const char *label;
void *data; void *data;
struct list_head list; struct list_head list;
struct blocking_notifier_head notifier;
#ifdef CONFIG_PINCTRL #ifdef CONFIG_PINCTRL
/* /*
@ -73,138 +69,36 @@ struct gpio_device {
#endif #endif
}; };
/** /* gpio suffixes used for ACPI and device tree lookup */
* struct acpi_gpio_info - ACPI GPIO specific information static __maybe_unused const char *const gpio_suffixes[] = { "gpios", "gpio" };
* @adev: reference to ACPI device which consumes GPIO resource
* @flags: GPIO initialization flags struct gpio_array {
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo struct gpio_desc **desc;
* @polarity: interrupt polarity as provided by ACPI unsigned int size;
* @triggering: triggering type as provided by ACPI struct gpio_chip *chip;
* @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping unsigned long *get_mask;
*/ unsigned long *set_mask;
struct acpi_gpio_info { unsigned long invert_mask[];
struct acpi_device *adev;
enum gpiod_flags flags;
bool gpioint;
int polarity;
int triggering;
unsigned int quirks;
}; };
/* gpio suffixes used for ACPI and device tree lookup */ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" };
#ifdef CONFIG_OF_GPIO
struct gpio_desc *of_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpio_lookup_flags *flags);
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *list_name, int index, enum of_gpio_flags *flags);
int of_gpiochip_add(struct gpio_chip *gc);
void of_gpiochip_remove(struct gpio_chip *gc);
#else
static inline struct gpio_desc *of_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpio_lookup_flags *flags)
{
return ERR_PTR(-ENOENT);
}
static inline struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
const char *list_name, int index, enum of_gpio_flags *flags)
{
return ERR_PTR(-ENOENT);
}
static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; }
static inline void of_gpiochip_remove(struct gpio_chip *gc) { }
#endif /* CONFIG_OF_GPIO */
#ifdef CONFIG_ACPI
void acpi_gpiochip_add(struct gpio_chip *chip);
void acpi_gpiochip_remove(struct gpio_chip *chip);
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags,
struct acpi_gpio_info *info);
struct gpio_desc *acpi_find_gpio(struct device *dev,
const char *con_id,
unsigned int idx,
enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags);
struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
const char *propname, int index,
struct acpi_gpio_info *info);
int acpi_gpio_count(struct device *dev, const char *con_id);
bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
#else
static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
static inline void
acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { }
static inline void
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
static inline int
acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info)
{
return 0;
}
static inline struct gpio_desc *
acpi_find_gpio(struct device *dev, const char *con_id,
unsigned int idx, enum gpiod_flags *dflags,
enum gpio_lookup_flags *lookupflags)
{
return ERR_PTR(-ENOENT);
}
static inline struct gpio_desc *
acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname,
int index, struct acpi_gpio_info *info)
{
return ERR_PTR(-ENXIO);
}
static inline int acpi_gpio_count(struct device *dev, const char *con_id)
{
return -ENODEV;
}
static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
const char *con_id)
{
return false;
}
#endif
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep, int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size, unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array); struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep, int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size, unsigned int array_size,
struct gpio_desc **desc_array, struct gpio_desc **desc_array,
int *value_array); struct gpio_array *array_info,
unsigned long *value_bitmap);
/* This is just passed between gpiolib and devres */ extern spinlock_t gpio_lock;
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
const char *propname, int index,
enum gpiod_flags dflags,
const char *label);
extern struct spinlock gpio_lock;
extern struct list_head gpio_devices; extern struct list_head gpio_devices;
struct gpio_desc { struct gpio_desc {
struct gpio_device *gdev; struct gpio_device *gdev;
unsigned long flags; unsigned long flags;
/* flag symbols are bit numbers */ /* flag symbols are bit numbers */
#define FLAG_REQUESTED 0 #define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1 #define FLAG_IS_OUT 1
@ -214,21 +108,34 @@ struct gpio_desc {
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
/* Connection label */ /* Connection label */
const char *label; const char *label;
/* Name of the GPIO */ /* Name of the GPIO */
const char *name; const char *name;
#ifdef CONFIG_OF_DYNAMIC
struct device_node *hog;
#endif
#ifdef CONFIG_GPIO_CDEV
/* debounce period in microseconds */
unsigned int debounce_period_us;
#endif
}; };
int gpiod_request(struct gpio_desc *desc, const char *label); int gpiod_request(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc); void gpiod_free(struct gpio_desc *desc);
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
unsigned long lflags, enum gpiod_flags dflags); unsigned long lflags, enum gpiod_flags dflags);
int gpiod_hog(struct gpio_desc *desc, const char *name, int gpiod_hog(struct gpio_desc *desc, const char *name,
unsigned long lflags, enum gpiod_flags dflags); unsigned long lflags, enum gpiod_flags dflags);
/* /*
* Return the GPIO number of the passed descriptor relative to its chip * Return the GPIO number of the passed descriptor relative to its chip
@ -238,9 +145,6 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
return desc - &desc->gdev->descs[0]; return desc - &desc->gdev->descs[0];
} }
void devprop_gpiochip_set_names(struct gpio_chip *chip,
const struct fwnode_handle *fwnode);
/* With descriptor prefix */ /* With descriptor prefix */
#define gpiod_emerg(desc, fmt, ...) \ #define gpiod_emerg(desc, fmt, ...) \
@ -264,35 +168,17 @@ void devprop_gpiochip_set_names(struct gpio_chip *chip,
/* With chip prefix */ /* With chip prefix */
#define chip_emerg(chip, fmt, ...) \ #define chip_emerg(gc, fmt, ...) \
dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) dev_emerg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_crit(chip, fmt, ...) \ #define chip_crit(gc, fmt, ...) \
dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) dev_crit(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_err(chip, fmt, ...) \ #define chip_err(gc, fmt, ...) \
dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_warn(chip, fmt, ...) \ #define chip_warn(gc, fmt, ...) \
dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_info(chip, fmt, ...) \ #define chip_info(gc, fmt, ...) \
dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#define chip_dbg(chip, fmt, ...) \ #define chip_dbg(gc, fmt, ...) \
dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__) dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
#ifdef CONFIG_GPIO_SYSFS
int gpiochip_sysfs_register(struct gpio_device *gdev);
void gpiochip_sysfs_unregister(struct gpio_device *gdev);
#else
static inline int gpiochip_sysfs_register(struct gpio_device *gdev)
{
return 0;
}
static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev)
{
}
#endif /* CONFIG_GPIO_SYSFS */
#endif /* GPIOLIB_H */ #endif /* GPIOLIB_H */

147
platform/centec-arm64/tsingma-bsp/src/i2c-ctc/i2c-ctc.c Normal file → Executable file
View File

@ -1,4 +1,5 @@
/* Centec I2C controller driver /*
* Centec I2C controller driver
* *
* Author: Wangyb <wangyb@centecnetworks.com> * Author: Wangyb <wangyb@centecnetworks.com>
* *
@ -20,6 +21,10 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/export.h> #include <linux/export.h>
#include "i2c-ctc.h" #include "i2c-ctc.h"
#include "../pinctrl-ctc/pinctrl-ctc.h"
#include "../include/sysctl.h"
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#define IC_ICK_NS(f) (1000000000 / f) #define IC_ICK_NS(f) (1000000000 / f)
@ -30,7 +35,7 @@ static char *abort_sources[] = {
[ABRT_10ADDR2_NOACK] = [ABRT_10ADDR2_NOACK] =
"second address byte not acknowledged (10bit mode)", "second address byte not acknowledged (10bit mode)",
[ABRT_TXDATA_NOACK] = "data not acknowledged", [ABRT_TXDATA_NOACK] = "data not acknowledged",
[ABRT_GCALL_NOACK] = "no acknowledgment for a general call", [ABRT_GCALL_NOACK] = "no acknowledgement for a general call",
[ABRT_GCALL_READ] = "read after general call", [ABRT_GCALL_READ] = "read after general call",
[ABRT_SBYTE_ACKDET] = "start byte acknowledged", [ABRT_SBYTE_ACKDET] = "start byte acknowledged",
[ABRT_SBYTE_NORSTRT] = [ABRT_SBYTE_NORSTRT] =
@ -99,8 +104,7 @@ int i2c_ctc_init(struct ctc_i2c_dev *dev)
__i2c_ctc_enable(dev, false); __i2c_ctc_enable(dev, false);
/* Set SCL timing parameters */ /* Set SCL timing parameters */
if ((dev->master_cfg & CTC_IC_CON_SPEED_MASK) if ((dev->master_cfg & CTC_IC_CON_SPEED_MASK) == CTC_IC_CON_SPEED_FAST) {
== CTC_IC_CON_SPEED_FAST) {
hcnt = __ctc_calc_fs_cnt(dev->clk_freq) - 14 - 4; hcnt = __ctc_calc_fs_cnt(dev->clk_freq) - 14 - 4;
lcnt = __ctc_calc_fs_cnt(dev->clk_freq) - 1 - 2; lcnt = __ctc_calc_fs_cnt(dev->clk_freq) - 1 - 2;
@ -122,8 +126,9 @@ int i2c_ctc_init(struct ctc_i2c_dev *dev)
} }
/* Configure SDA Hold Time if required */ /* Configure SDA Hold Time if required */
if (dev->sda_hold_time) if (dev->sda_hold_time) {
ctc_writel(dev, dev->sda_hold_time, CTC_IC_SDA_HOLD); ctc_writel(dev, dev->sda_hold_time, CTC_IC_SDA_HOLD);
}
/* Configure Tx/Rx FIFO threshold levels */ /* Configure Tx/Rx FIFO threshold levels */
comp_param1 = ctc_readl(dev, CTC_IC_COMP_PARAM_1); comp_param1 = ctc_readl(dev, CTC_IC_COMP_PARAM_1);
@ -139,6 +144,33 @@ int i2c_ctc_init(struct ctc_i2c_dev *dev)
return 0; return 0;
} }
int i2c_ctc_recover_bus(struct i2c_adapter *adap)
{
struct ctc_i2c_dev *dev = i2c_get_adapdata(adap);
u32 val = 0;
dev_info(dev->dev, "Trying i2c bus recovery\n");
if (dev->i2c_num == 0)
val = 0x1;
if (dev->i2c_num == 1)
val = 0x2;
regmap_write(dev->regmap_base,
offsetof(struct SysCtl_regs, SysI2CResetCtl), val);
val = 0x0;
regmap_write(dev->regmap_base,
offsetof(struct SysCtl_regs, SysI2CResetCtl), val);
if (dev->soc_ver == CTC_REV_TM_1_1) {
ctc_writel(dev, 0x1, CTC_IC_BUS_CLEAR_EN);
}
i2c_ctc_init(dev);
return 0;
}
static int i2c_ctc_wait_bus_not_busy(struct ctc_i2c_dev *dev) static int i2c_ctc_wait_bus_not_busy(struct ctc_i2c_dev *dev)
{ {
int timeout = 20; int timeout = 20;
@ -146,7 +178,15 @@ static int i2c_ctc_wait_bus_not_busy(struct ctc_i2c_dev *dev)
while (ctc_readl(dev, CTC_IC_STATUS) & CTC_IC_STATUS_ACTIVITY) { while (ctc_readl(dev, CTC_IC_STATUS) & CTC_IC_STATUS_ACTIVITY) {
if (timeout <= 0) { if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n"); dev_warn(dev->dev, "timeout waiting for bus ready\n");
return -ETIMEDOUT; i2c_recover_bus(&dev->adapter);
if (ctc_readl(dev, CTC_IC_STATUS) &
CTC_IC_STATUS_ACTIVITY) {
dev_warn(dev->dev,
"timeout waiting for bus ready again\n");
return -ETIMEDOUT;
}
return 0;
} }
timeout--; timeout--;
usleep_range(1000, 1100); usleep_range(1000, 1100);
@ -154,6 +194,16 @@ static int i2c_ctc_wait_bus_not_busy(struct ctc_i2c_dev *dev)
return 0; return 0;
} }
void i2c_ctc_disable(struct ctc_i2c_dev *dev)
{
/* Disable controller */
__i2c_ctc_enable(dev, false);
/* Disable all interupts */
ctc_writel(dev, 0, CTC_IC_INTR_MASK);
ctc_readl(dev, CTC_IC_CLR_INTR);
}
void i2c_ctc_disable_intr(struct ctc_i2c_dev *dev) void i2c_ctc_disable_intr(struct ctc_i2c_dev *dev)
{ {
ctc_writel(dev, 0, CTC_IC_INTR_MASK); ctc_writel(dev, 0, CTC_IC_INTR_MASK);
@ -189,7 +239,7 @@ static void i2c_ctc_xfer_init(struct ctc_i2c_dev *dev)
/* Clear and enable interrupts */ /* Clear and enable interrupts */
ctc_readl(dev, CTC_IC_CLR_INTR); ctc_readl(dev, CTC_IC_CLR_INTR);
ctc_writel(dev, CTC_IC_INTR_DEFAULT_MASK, CTC_IC_INTR_MASK); ctc_writel(dev, CTC_IC_INTR_MASTER_MASK, CTC_IC_INTR_MASK);
} }
static int i2c_ctc_handle_tx_abort(struct ctc_i2c_dev *dev) static int i2c_ctc_handle_tx_abort(struct ctc_i2c_dev *dev)
@ -199,13 +249,12 @@ static int i2c_ctc_handle_tx_abort(struct ctc_i2c_dev *dev)
if (abort_source & CTC_IC_TX_ABRT_NOACK) { if (abort_source & CTC_IC_TX_ABRT_NOACK) {
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_dbg(dev->dev, "%s: %s\n", __func__, dev_dbg(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
abort_sources[i]);
return -EREMOTEIO; return -EREMOTEIO;
} }
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
if (abort_source & CTC_IC_TX_ARB_LOST) if (abort_source & CTC_IC_TX_ARB_LOST)
return -EAGAIN; return -EAGAIN;
@ -241,7 +290,7 @@ static int i2c_ctc_interrupt_transfer(struct ctc_i2c_dev *dev)
/* wait for tx to complete */ /* wait for tx to complete */
if (!wait_for_completion_timeout(&dev->cmd_complete, HZ)) { if (!wait_for_completion_timeout(&dev->cmd_complete, HZ)) {
dev_err(dev->dev, "controller timed out\n"); dev_err(dev->dev, "controller timed out\n");
i2c_ctc_init(dev); i2c_recover_bus(&dev->adapter);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
goto done; goto done;
} }
@ -287,8 +336,7 @@ static int ctc_i2c_xfer_finish(struct ctc_i2c_dev *dev)
CTC_IC_INTR_STOP_DET)) { CTC_IC_INTR_STOP_DET)) {
ctc_readl(dev, CTC_IC_CLR_STOP_DET); ctc_readl(dev, CTC_IC_CLR_STOP_DET);
break; break;
} else if (time_after(jiffies, start_stop_det + } else if (time_after(jiffies, start_stop_det + I2C_STOPDET_TO)) {
I2C_STOPDET_TO)) {
break; break;
} }
} }
@ -302,8 +350,8 @@ static int ctc_i2c_xfer_finish(struct ctc_i2c_dev *dev)
return 0; return 0;
} }
static int __ctc_i2c_read(struct ctc_i2c_dev *dev, __u16 chip_addr, u8 *offset, static int __ctc_i2c_read(struct ctc_i2c_dev *dev, __u16 chip_addr, u8 * offset,
__u16 olen, u8 *data, __u16 dlen) __u16 olen, u8 * data, __u16 dlen)
{ {
unsigned int active = 0; unsigned int active = 0;
unsigned int flag = 0; unsigned int flag = 0;
@ -371,7 +419,7 @@ static int __ctc_i2c_read(struct ctc_i2c_dev *dev, __u16 chip_addr, u8 *offset,
} }
static int __ctc_i2c_write(struct ctc_i2c_dev *dev, __u16 chip_addr, static int __ctc_i2c_write(struct ctc_i2c_dev *dev, __u16 chip_addr,
u8 *offset, __u16 olen, u8 *data, __u16 dlen) u8 * offset, __u16 olen, u8 * data, __u16 dlen)
{ {
int ret; int ret;
unsigned long start_time_tx; unsigned long start_time_tx;
@ -401,8 +449,9 @@ static int __ctc_i2c_write(struct ctc_i2c_dev *dev, __u16 chip_addr,
} }
data++; data++;
start_time_tx = jiffies; start_time_tx = jiffies;
} else if (time_after(jiffies, start_time_tx + } else
(nb * I2C_BYTE_TO))) { if (time_after(jiffies, start_time_tx + (nb * I2C_BYTE_TO)))
{
dev_err(dev->dev, "Timed out. i2c write Failed\n"); dev_err(dev->dev, "Timed out. i2c write Failed\n");
return -ETIMEDOUT; return -ETIMEDOUT;
} }
@ -418,8 +467,7 @@ static int i2c_ctc_polling_transfer(struct ctc_i2c_dev *dev)
memset(&dummy, 0, sizeof(struct i2c_msg)); memset(&dummy, 0, sizeof(struct i2c_msg));
/* We expect either two messages (one with an offset and one with the /* We expect either two messages (one with an offset and one with the
* actucal data) or one message (just data) * actucal data) or one message (just data) */
*/
if (dev->msgs_num > 2 || dev->msgs_num == 0) { if (dev->msgs_num > 2 || dev->msgs_num == 0) {
dev_err(dev->dev, "%s: Only one or two messages are supported.", dev_err(dev->dev, "%s: Only one or two messages are supported.",
__func__); __func__);
@ -469,7 +517,7 @@ static void i2c_ctc_xfer_msg(struct ctc_i2c_dev *dev)
u8 *buf = dev->tx_buf; u8 *buf = dev->tx_buf;
bool need_restart = false; bool need_restart = false;
intr_mask = CTC_IC_INTR_DEFAULT_MASK; intr_mask = CTC_IC_INTR_MASTER_MASK;
/* msg_write_idx */ /* msg_write_idx */
for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
@ -519,14 +567,11 @@ static void i2c_ctc_xfer_msg(struct ctc_i2c_dev *dev)
if (rx_limit - dev->rx_outstanding <= 0) if (rx_limit - dev->rx_outstanding <= 0)
break; break;
/* 1 = Read */ ctc_writel(dev, cmd | CTC_CMD_READ, CTC_IC_DATA_CMD); /* 1 = Read */
ctc_writel(dev, cmd | CTC_CMD_READ,
CTC_IC_DATA_CMD);
rx_limit--; rx_limit--;
dev->rx_outstanding++; dev->rx_outstanding++;
} else } else
/* 0 = Write */ ctc_writel(dev, cmd | *buf++, CTC_IC_DATA_CMD); /* 0 = Write */
ctc_writel(dev, cmd | *buf++, CTC_IC_DATA_CMD);
tx_limit--; tx_limit--;
buf_len--; buf_len--;
} }
@ -538,8 +583,8 @@ static void i2c_ctc_xfer_msg(struct ctc_i2c_dev *dev)
/* more bytes to be written */ /* more bytes to be written */
dev->status |= STATUS_WRITE_IN_PROGRESS; dev->status |= STATUS_WRITE_IN_PROGRESS;
break; break;
} } else
dev->status &= ~STATUS_WRITE_IN_PROGRESS; dev->status &= ~STATUS_WRITE_IN_PROGRESS;
} }
if (dev->msg_write_idx == dev->msgs_num) if (dev->msg_write_idx == dev->msgs_num)
@ -584,8 +629,8 @@ static void i2c_ctc_read(struct ctc_i2c_dev *dev)
dev->rx_buf_len = len; dev->rx_buf_len = len;
dev->rx_buf = buf; dev->rx_buf = buf;
return; return;
} } else
dev->status &= ~STATUS_READ_IN_PROGRESS; dev->status &= ~STATUS_READ_IN_PROGRESS;
} }
} }
@ -627,6 +672,7 @@ static irqreturn_t i2c_ctc_isr(int this_irq, void *dev_id)
enabled = ctc_readl(dev, CTC_IC_ENABLE); enabled = ctc_readl(dev, CTC_IC_ENABLE);
stat = ctc_readl(dev, CTC_IC_RAW_INTR_STAT); stat = ctc_readl(dev, CTC_IC_RAW_INTR_STAT);
dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled, dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled,
stat); stat);
if (!enabled || !(stat & ~CTC_IC_INTR_ACTIVITY)) if (!enabled || !(stat & ~CTC_IC_INTR_ACTIVITY))
@ -658,7 +704,6 @@ tx_aborted:
static u32 i2c_ctc_func(struct i2c_adapter *adap) static u32 i2c_ctc_func(struct i2c_adapter *adap)
{ {
struct ctc_i2c_dev *dev = i2c_get_adapdata(adap); struct ctc_i2c_dev *dev = i2c_get_adapdata(adap);
return dev->functionality; return dev->functionality;
} }
@ -667,6 +712,10 @@ static struct i2c_algorithm i2c_ctc_algo = {
.functionality = i2c_ctc_func, .functionality = i2c_ctc_func,
}; };
static struct i2c_bus_recovery_info i2c_ctc_recovery_info = {
.recover_bus = i2c_ctc_recover_bus,
};
int i2c_ctc_probe(struct ctc_i2c_dev *dev) int i2c_ctc_probe(struct ctc_i2c_dev *dev)
{ {
struct i2c_adapter *adap = &dev->adapter; struct i2c_adapter *adap = &dev->adapter;
@ -680,6 +729,7 @@ int i2c_ctc_probe(struct ctc_i2c_dev *dev)
snprintf(adap->name, sizeof(adap->name), snprintf(adap->name, sizeof(adap->name),
"Centec TsingMa SoC's I2C adapter"); "Centec TsingMa SoC's I2C adapter");
adap->algo = &i2c_ctc_algo; adap->algo = &i2c_ctc_algo;
adap->bus_recovery_info = &i2c_ctc_recovery_info;
adap->dev.parent = dev->dev; adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev); i2c_set_adapdata(adap, dev);
@ -708,6 +758,7 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
struct resource *mem; struct resource *mem;
int irq, ret; int irq, ret;
u32 clk_freq, ht; u32 clk_freq, ht;
u32 val, i2c_num;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) if (irq < 0)
@ -722,6 +773,15 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
if (IS_ERR(dev->base)) if (IS_ERR(dev->base))
return PTR_ERR(dev->base); return PTR_ERR(dev->base);
dev->regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(dev->regmap_base))
return PTR_ERR(dev->regmap_base);
regmap_read(dev->regmap_base,
offsetof(struct SysCtl_regs, SysCtlSysRev), &val);
dev->soc_ver = ((val == 0x1) ? CTC_REV_TM_1_1 : CTC_REV_TM_1_0);
dev->dev = &pdev->dev; dev->dev = &pdev->dev;
dev->irq = irq; dev->irq = irq;
platform_set_drvdata(pdev, dev); platform_set_drvdata(pdev, dev);
@ -738,17 +798,6 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
dev->master_cfg |=
CTC_IC_CON_MASTER | CTC_IC_CON_SLAVE_DISABLE |
CTC_IC_CON_RESTART_EN;
dev->functionality =
I2C_FUNC_I2C |
I2C_FUNC_10BIT_ADDR |
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
dev->clk = devm_clk_get(&pdev->dev, NULL); dev->clk = devm_clk_get(&pdev->dev, NULL);
clk_prepare_enable(dev->clk); clk_prepare_enable(dev->clk);
@ -757,10 +806,8 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
dev->sda_hold_time = ht / IC_ICK_NS(clk_get_rate(dev->clk)); dev->sda_hold_time = ht / IC_ICK_NS(clk_get_rate(dev->clk));
} }
if (of_property_read_bool(pdev->dev.of_node, "i2c-polling-xfer")) of_property_read_u32(pdev->dev.of_node, "i2c-num", &i2c_num);
dev->xfer_type = CTC_IC_POLLING_TRANSFER; dev->i2c_num = i2c_num;
else
dev->xfer_type = CTC_IC_INTERRUPT_TRANSFER;
dev->adapter.nr = pdev->id; dev->adapter.nr = pdev->id;
adap = &dev->adapter; adap = &dev->adapter;
@ -769,6 +816,7 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
adap->dev.of_node = pdev->dev.of_node; adap->dev.of_node = pdev->dev.of_node;
ret = i2c_ctc_probe(dev); ret = i2c_ctc_probe(dev);
return ret; return ret;
} }
@ -778,12 +826,7 @@ static int ctc_i2c_plat_remove(struct platform_device *pdev)
i2c_del_adapter(&dev->adapter); i2c_del_adapter(&dev->adapter);
/* Disable controller */ i2c_ctc_disable(dev);
__i2c_ctc_enable(dev, false);
/* Disable all interupts */
ctc_writel(dev, 0, CTC_IC_INTR_MASK);
ctc_readl(dev, CTC_IC_CLR_INTR);
return 0; return 0;
} }

View File

@ -1,4 +1,5 @@
/* Author: Wangyb <wangyb@centecnetworks.com> /*
* Author: Wangyb <wangyb@centecnetworks.com>
* *
* Copyright 2005-2018, Centec Networks (Suzhou) Co., Ltd. * Copyright 2005-2018, Centec Networks (Suzhou) Co., Ltd.
* *
@ -71,7 +72,6 @@
#define CTC_IC_INTR_GEN_CALL 0x800 #define CTC_IC_INTR_GEN_CALL 0x800
#define CTC_IC_INTR_DEFAULT_MASK (CTC_IC_INTR_RX_FULL | \ #define CTC_IC_INTR_DEFAULT_MASK (CTC_IC_INTR_RX_FULL | \
CTC_IC_INTR_TX_EMPTY | \
CTC_IC_INTR_TX_ABRT | \ CTC_IC_INTR_TX_ABRT | \
CTC_IC_INTR_STOP_DET) CTC_IC_INTR_STOP_DET)
@ -112,7 +112,7 @@
CTC_IC_TX_ABRT_TXDATA_NOACK | \ CTC_IC_TX_ABRT_TXDATA_NOACK | \
CTC_IC_TX_ABRT_GCALL_NOACK) CTC_IC_TX_ABRT_GCALL_NOACK)
#define CTC_CMD_READ 0x0100 #define CTC_CMD_READ 0x0100
#define CTC_STOP 0x0200 #define CTC_STOP 0x0200
#define CTC_RESTART 0x0400 #define CTC_RESTART 0x0400
@ -125,6 +125,38 @@
#define CTC_IC_STATUS_MA 0x0020 #define CTC_IC_STATUS_MA 0x0020
#define CTC_IC_STATUS_TFE 0x0004 #define CTC_IC_STATUS_TFE 0x0004
#define CTC_IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \
I2C_FUNC_SMBUS_BYTE | \
I2C_FUNC_SMBUS_BYTE_DATA | \
I2C_FUNC_SMBUS_WORD_DATA | \
I2C_FUNC_SMBUS_BLOCK_DATA | \
I2C_FUNC_SMBUS_I2C_BLOCK)
#define CTC_IC_INTR_MASTER_MASK (CTC_IC_INTR_DEFAULT_MASK | \
CTC_IC_INTR_TX_EMPTY)
#define CTC_IC_INTR_SLAVE_MASK (CTC_IC_INTR_DEFAULT_MASK | \
CTC_IC_INTR_RX_DONE | \
CTC_IC_INTR_RX_UNDER | \
CTC_IC_INTR_RD_REQ)
#define CTC_IC_CON_STOP_DET_IFADDRESSED 0x80
#define CTC_IC_CON_TX_EMPTY_CTRL 0x100
#define CTC_IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200
#define CTC_IC_SAR 0x8
#define CTC_IC_STATUS_SLAVE_ACTIVITY BIT(6)
#define CTC_IC_BUS_CLEAR_EN 0xb0
#define CTC_IC_BUS_CLEAR_THRD 0xb4
/*
* operation modes
*/
#define CTC_IC_MASTER 0
#define CTC_IC_SLAVE 1
enum xfer_type_e { enum xfer_type_e {
CTC_IC_INTERRUPT_TRANSFER, CTC_IC_INTERRUPT_TRANSFER,
CTC_IC_POLLING_TRANSFER CTC_IC_POLLING_TRANSFER
@ -159,4 +191,15 @@ struct ctc_i2c_dev {
u32 clk_freq; u32 clk_freq;
u32 sda_hold_time; u32 sda_hold_time;
u32 xfer_type; u32 xfer_type;
u32 mode;
u32 slave_cfg;
void (*disable) (struct ctc_i2c_dev * dev);
void (*disable_int) (struct ctc_i2c_dev * dev);
int (*init) (struct ctc_i2c_dev * dev);
struct i2c_client *slave;
struct regmap *regmap_base;
u32 soc_ver;
#define CTC_REV_TM_1_0 0x0
#define CTC_REV_TM_1_1 0x1
u32 i2c_num;
}; };

View File

@ -6,7 +6,7 @@
#define SYS_TSINGMA_TEMP_TABLE_NUM 166 #define SYS_TSINGMA_TEMP_TABLE_NUM 166
#define SYS_TSINGMA_SENSOR_TIMEOUT 1000 #define SYS_TSINGMA_SENSOR_TIMEOUT 1000
struct ctc_switch_cmd_status_t { typedef struct ctc_switch_cmd_status_s {
u32 cmdReadType:1; u32 cmdReadType:1;
u32 pcieReqCmdChk:3; u32 pcieReqCmdChk:3;
u32 cmdEntryWords:4; u32 cmdEntryWords:4;
@ -24,20 +24,20 @@ struct ctc_switch_cmd_status_t {
u32 pciePoisoned:1; u32 pciePoisoned:1;
u32 regProcState:3; u32 regProcState:3;
u32 pcieReqOverlap:1; u32 pcieReqOverlap:1;
}; } ctc_switch_cmd_status_t;
union ctc_switch_cmd_status_u_t { typedef union drv_pci_cmd_status_u_e {
struct ctc_switch_cmd_status_t cmd_status; ctc_switch_cmd_status_t cmd_status;
u32 val; u32 val;
}; } ctc_switch_cmd_status_u_t;
struct ctc_access_t { typedef struct ctc_access_s {
u32 cmd_status; u32 cmd_status;
u32 addr; u32 addr;
u32 data[16]; u32 data[16];
}; } ctc_access_t;
extern int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value); extern int ctc5236_switch_read(u32 offset, u32 len, u32 * p_value);
extern int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value); extern int ctc5236_switch_write(u32 offset, u32 len, u32 * p_value);
extern int get_switch_temperature(void); extern int get_switch_temperature(void);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Core private header for the pin control subsystem * Core private header for the pin control subsystem
* *
@ -5,8 +6,6 @@
* Written on behalf of Linaro for ST-Ericsson * Written on behalf of Linaro for ST-Ericsson
* *
* Author: Linus Walleij <linus.walleij@linaro.org> * Author: Linus Walleij <linus.walleij@linaro.org>
*
* License terms: GNU General Public License (GPL) version 2
*/ */
#include <linux/kref.h> #include <linux/kref.h>
@ -218,7 +217,7 @@ int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name,
int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev, int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev,
unsigned int group_selector); unsigned int group_selector);
#endif /* CONFIG_GENERIC_PINCTRL_GROUPS */ #endif /* CONFIG_GENERIC_PINCTRL_GROUPS */
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name); struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np); struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np);
@ -233,13 +232,12 @@ static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev,
return radix_tree_lookup(&pctldev->pin_desc_tree, pin); return radix_tree_lookup(&pctldev->pin_desc_tree, pin);
} }
extern struct pinctrl_gpio_range * extern struct pinctrl_gpio_range *pinctrl_find_gpio_range_from_pin_nolock(struct
pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev, pinctrl_dev
unsigned int pin); *pctldev,
unsigned
int pinctrl_register_map(const struct pinctrl_map *maps, unsigned num_maps, int
bool dup); pin);
void pinctrl_unregister_map(const struct pinctrl_map *map);
extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev); extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
extern int pinctrl_force_default(struct pinctrl_dev *pctldev); extern int pinctrl_force_default(struct pinctrl_dev *pctldev);

View File

@ -1,3 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Internal interface between the core pin control system and the * Internal interface between the core pin control system and the
* pin config portions * pin config portions
@ -7,8 +8,6 @@
* Based on bits of regulator core, gpio core and clk core * Based on bits of regulator core, gpio core and clk core
* *
* Author: Linus Walleij <linus.walleij@linaro.org> * Author: Linus Walleij <linus.walleij@linaro.org>
*
* License terms: GNU General Public License (GPL) version 2
*/ */
#ifdef CONFIG_PINCONF #ifdef CONFIG_PINCONF
@ -16,7 +15,7 @@
int pinconf_check_ops(struct pinctrl_dev *pctldev); int pinconf_check_ops(struct pinctrl_dev *pctldev);
int pinconf_validate_map(const struct pinctrl_map *map, int i); int pinconf_validate_map(const struct pinctrl_map *map, int i);
int pinconf_map_to_setting(const struct pinctrl_map *map, int pinconf_map_to_setting(const struct pinctrl_map *map,
struct pinctrl_setting *setting); struct pinctrl_setting *setting);
void pinconf_free_setting(const struct pinctrl_setting *setting); void pinconf_free_setting(const struct pinctrl_setting *setting);
int pinconf_apply_setting(const struct pinctrl_setting *setting); int pinconf_apply_setting(const struct pinctrl_setting *setting);
@ -45,7 +44,7 @@ static inline int pinconf_validate_map(const struct pinctrl_map *map, int i)
} }
static inline int pinconf_map_to_setting(const struct pinctrl_map *map, static inline int pinconf_map_to_setting(const struct pinctrl_map *map,
struct pinctrl_setting *setting) struct pinctrl_setting *setting)
{ {
return 0; return 0;
} }

View File

@ -8,7 +8,6 @@
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/bitops.h> #include <linux/bitops.h>
@ -24,6 +23,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h>
#include "../include/sysctl.h" #include "../include/sysctl.h"
#include "pinctrl-ctc.h" #include "pinctrl-ctc.h"
#include "core.h" #include "core.h"
@ -87,12 +87,13 @@ static void ctc_pinctrl_child_count(struct ctc_pinctrl *info,
} }
static struct ctc_pin_bank *ctc_bank_num_to_bank(struct ctc_pinctrl *info, static struct ctc_pin_bank *ctc_bank_num_to_bank(struct ctc_pinctrl *info,
unsigned int num) unsigned num)
{ {
struct ctc_pin_bank *b = info->ctrl->pin_banks; struct ctc_pin_bank *b = info->ctrl->pin_banks;
if (num < info->ctrl->nr_banks) if (num < info->ctrl->nr_banks) {
return &b[num]; return &b[num];
}
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
@ -195,8 +196,10 @@ static int ctc_pinctrl_parse_dt(struct platform_device *pdev,
struct device_node *child; struct device_node *child;
info->ctrl = devm_kzalloc(dev, sizeof(struct ctc_pin_ctrl), GFP_KERNEL); info->ctrl = devm_kzalloc(dev, sizeof(struct ctc_pin_ctrl), GFP_KERNEL);
if (!info->ctrl) if (!info->ctrl) {
dev_err(dev, "failed to allocate memory for pin\n");
return -EINVAL; return -EINVAL;
}
ret = of_property_read_u32(np, "ctc,pinctrl-bank0", &bank0_pins); ret = of_property_read_u32(np, "ctc,pinctrl-bank0", &bank0_pins);
if (ret < 0) { if (ret < 0) {
@ -229,13 +232,17 @@ static int ctc_pinctrl_parse_dt(struct platform_device *pdev,
info->functions = devm_kzalloc(dev, info->nfunctions * info->functions = devm_kzalloc(dev, info->nfunctions *
sizeof(struct ctc_pmx_func), GFP_KERNEL); sizeof(struct ctc_pmx_func), GFP_KERNEL);
if (!info->functions) if (!info->functions) {
dev_err(dev, "failed to allocate memory for function list\n");
return -EINVAL; return -EINVAL;
}
info->groups = devm_kzalloc(dev, info->ngroups * info->groups = devm_kzalloc(dev, info->ngroups *
sizeof(struct ctc_pin_group), GFP_KERNEL); sizeof(struct ctc_pin_group), GFP_KERNEL);
if (!info->groups) if (!info->groups) {
dev_err(dev, "failed allocate memory for ping group list\n");
return -EINVAL; return -EINVAL;
}
i = 0; i = 0;
for_each_child_of_node(np, child) { for_each_child_of_node(np, child) {
@ -258,7 +265,7 @@ static int ctc_get_groups_count(struct pinctrl_dev *pctldev)
} }
static const char *ctc_get_group_name(struct pinctrl_dev *pctldev, static const char *ctc_get_group_name(struct pinctrl_dev *pctldev,
unsigned int selector) unsigned selector)
{ {
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
@ -266,8 +273,8 @@ static const char *ctc_get_group_name(struct pinctrl_dev *pctldev,
} }
static int ctc_get_group_pins(struct pinctrl_dev *pctldev, static int ctc_get_group_pins(struct pinctrl_dev *pctldev,
unsigned int selector, const unsigned int **pins, unsigned selector, const unsigned **pins,
unsigned int *npins) unsigned *npins)
{ {
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
@ -298,7 +305,7 @@ static inline const struct ctc_pin_group *ctc_pinctrl_name_to_group(const struct
static int ctc_dt_node_to_map(struct pinctrl_dev *pctldev, static int ctc_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np, struct device_node *np,
struct pinctrl_map **map, unsigned int *num_maps) struct pinctrl_map **map, unsigned *num_maps)
{ {
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
const struct ctc_pin_group *grp; const struct ctc_pin_group *grp;
@ -348,7 +355,7 @@ static int ctc_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
} }
static const char *ctc_pmx_get_func_name(struct pinctrl_dev *pctldev, static const char *ctc_pmx_get_func_name(struct pinctrl_dev *pctldev,
unsigned int selector) unsigned selector)
{ {
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
@ -356,8 +363,8 @@ static const char *ctc_pmx_get_func_name(struct pinctrl_dev *pctldev,
} }
static int ctc_pmx_get_groups(struct pinctrl_dev *pctldev, static int ctc_pmx_get_groups(struct pinctrl_dev *pctldev,
unsigned int selector, const char *const **groups, unsigned selector, const char *const **groups,
unsigned int *const num_groups) unsigned *const num_groups)
{ {
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
@ -368,7 +375,7 @@ static int ctc_pmx_get_groups(struct pinctrl_dev *pctldev,
} }
static struct ctc_pin_bank *ctc_pin_to_bank(struct ctc_pinctrl *info, static struct ctc_pin_bank *ctc_pin_to_bank(struct ctc_pinctrl *info,
unsigned int pin) unsigned pin)
{ {
struct ctc_pin_bank *b = info->ctrl->pin_banks; struct ctc_pin_bank *b = info->ctrl->pin_banks;
@ -396,8 +403,8 @@ static int ctc_set_pin_mux(struct ctc_pinctrl *info, struct ctc_pin_bank *bank,
return 0; return 0;
} }
static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector, static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
unsigned int group) unsigned group)
{ {
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
const unsigned int *pins = info->groups[group].pins; const unsigned int *pins = info->groups[group].pins;
@ -408,7 +415,8 @@ static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector,
dev_dbg(info->dev, "enable function %s group %s\n", dev_dbg(info->dev, "enable function %s group %s\n",
info->functions[selector].name, info->groups[group].name); info->functions[selector].name, info->groups[group].name);
/* for each pin in the pin group selected, program the corresponding pin /*
* for each pin in the pin group selected, program the correspoding pin
* pin function number in the config register. * pin function number in the config register.
*/ */
for (cnt = 0; cnt < info->groups[group].npins; cnt++) { for (cnt = 0; cnt < info->groups[group].npins; cnt++) {
@ -423,7 +431,7 @@ static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector,
} }
static void ctc_dt_free_map(struct pinctrl_dev *pctldev, static void ctc_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map, unsigned int num_maps) struct pinctrl_map *map, unsigned num_maps)
{ {
} }
@ -528,7 +536,7 @@ static struct platform_driver ctc_pinctrl_driver = {
//static int __init ctc_pinctrl_drv_register(void) //static int __init ctc_pinctrl_drv_register(void)
//{ //{
// return platform_driver_register(&ctc_pinctrl_driver); // return platform_driver_register(&ctc_pinctrl_driver);
//} //}
// //
//postcore_initcall(ctc_pinctrl_drv_register); //postcore_initcall(ctc_pinctrl_drv_register);

51
platform/centec-arm64/tsingma-bsp/src/pwm-ctc/pwm-ctc.c Normal file → Executable file
View File

@ -1,4 +1,5 @@
/* Centec PWM driver /*
* Centec PWM driver
* *
* Author: wangyb <wangyb@centecnetworks.com> * Author: wangyb <wangyb@centecnetworks.com>
* *
@ -25,14 +26,14 @@
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#define CTC_NUM_PWM 4 #define CTC_NUM_PWM 4
#define CTC_CR_PWM 0x0 #define CTC_CR_PWM 0x0
#define CTC_DUTY_PWM 0x4 #define CTC_DUTY_PWM 0x4
#define CTC_MAX_PERIOD_PWM 0xFFFFFF #define CTC_MAX_PERIOD_PWM 0xFFFFFF
#define CTC_MAX_DUTY_PWM 0xFFFFFF #define CTC_MAX_DUTY_PWM 0xFFFFFF
#define CTC_PWM_ENABLE 0x80000000 #define CTC_PWM_ENABLE 0x80000000
#define CTC_PERIOD_TACH 0x0 #define CTC_PERIOD_TACH 0x0
#define CTC_DUTY_TACH 0x4 #define CTC_DUTY_TACH 0x4
@ -60,7 +61,6 @@ static inline u32 ctc_pwm_readl(struct ctc_pwm_chip *chip, unsigned int num,
unsigned long offset) unsigned long offset)
{ {
u32 val; u32 val;
regmap_read(chip->regmap_base, regmap_read(chip->regmap_base,
offsetof(struct SysCtl_regs, offsetof(struct SysCtl_regs,
SysPwmCtl) + offset + num * 0x8, &val); SysPwmCtl) + offset + num * 0x8, &val);
@ -71,7 +71,6 @@ static inline u32 ctc_tach_readl(struct ctc_pwm_chip *chip, unsigned int num,
unsigned long offset) unsigned long offset)
{ {
u32 val; u32 val;
regmap_read(chip->regmap_base, regmap_read(chip->regmap_base,
offsetof(struct SysCtl_regs, offsetof(struct SysCtl_regs,
SysTachLog) + offset + num * 0x8, &val); SysTachLog) + offset + num * 0x8, &val);
@ -83,19 +82,30 @@ static int ctc_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
{ {
struct ctc_pwm_chip *pc = to_ctc_pwm_chip(chip); struct ctc_pwm_chip *pc = to_ctc_pwm_chip(chip);
u32 cur_value; u32 cur_value;
u32 duty_cycle = 0;
u32 period_cycle = 0;
duty_ns = duty_ns / 1000; duty_cycle = duty_ns / 1000;
period_ns = period_ns / 1000; period_cycle = period_ns / 1000;
if (duty_cycle < 0 || period_cycle < 1)
return -1;
if (duty_cycle == 0) {
duty_cycle = 1;
} else if (duty_cycle == period_cycle) {
duty_cycle = duty_cycle - 1;
}
/* duty cycle */ /* duty cycle */
duty_ns = duty_ns & CTC_MAX_DUTY_PWM; duty_cycle = duty_cycle & CTC_MAX_DUTY_PWM;
ctc_pwm_writel(pc, pwm->hwpwm, CTC_DUTY_PWM, duty_ns); ctc_pwm_writel(pc, pwm->hwpwm, CTC_DUTY_PWM, duty_cycle);
/* period cycle */ /* period cycle */
period_ns = period_ns & CTC_MAX_PERIOD_PWM; period_cycle = period_cycle & CTC_MAX_PERIOD_PWM;
cur_value = ctc_pwm_readl(pc, pwm->hwpwm, CTC_CR_PWM); cur_value = ctc_pwm_readl(pc, pwm->hwpwm, CTC_CR_PWM);
cur_value &= ~(CTC_MAX_PERIOD_PWM); cur_value &= ~(CTC_MAX_PERIOD_PWM);
cur_value |= period_ns << 0; cur_value |= period_cycle << 0;
ctc_pwm_writel(pc, pwm->hwpwm, CTC_CR_PWM, cur_value); ctc_pwm_writel(pc, pwm->hwpwm, CTC_CR_PWM, cur_value);
return 0; return 0;
@ -139,11 +149,11 @@ static void ctc_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
state->polarity = PWM_POLARITY_NORMAL; state->polarity = PWM_POLARITY_NORMAL;
state->period = (cur_value & (~CTC_PWM_ENABLE)) * 1000; state->period = (cur_value & (~CTC_PWM_ENABLE)) * 1000; // in nanoseconds
cur_value_2 = ctc_pwm_readl(pc, pwm->hwpwm, CTC_DUTY_PWM); cur_value_2 = ctc_pwm_readl(pc, pwm->hwpwm, CTC_DUTY_PWM);
state->duty_cycle = cur_value_2 * 1000; state->duty_cycle = cur_value_2 * 1000; // in nanoseconds
} }
static int ctc_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, static int ctc_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
@ -154,10 +164,10 @@ static int ctc_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
u32 duty_tach; u32 duty_tach;
period_tach = ctc_tach_readl(pc, pwm->hwpwm, CTC_PERIOD_TACH) / 4; period_tach = ctc_tach_readl(pc, pwm->hwpwm, CTC_PERIOD_TACH) / 4;
result->period = period_tach * 1000; result->period = period_tach * 1000; // in nanoseconds
duty_tach = ctc_tach_readl(pc, pwm->hwpwm, CTC_DUTY_TACH) / 4; duty_tach = ctc_tach_readl(pc, pwm->hwpwm, CTC_DUTY_TACH) / 4;
result->duty_cycle = duty_tach * 1000; result->duty_cycle = duty_tach * 1000; // in nanoseconds
return 0; return 0;
} }
@ -188,12 +198,13 @@ static int ctc_pwm_probe(struct platform_device *pdev)
pc->regmap_base = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, pc->regmap_base = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"ctc,sysctrl"); "ctc,sysctrl");
pctldev = devm_kzalloc(&pdev->dev, sizeof(*pctldev), GFP_KERNEL); pctldev = devm_kzalloc(&pdev->dev, sizeof(*pctldev), GFP_KERNEL);
if (!pctldev) if (!pctldev) {
return -1; return -1;
}
pctldev->p = pinctrl_get(&pdev->dev); pctldev->p = pinctrl_get(&pdev->dev);
state = pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT); state = pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);
pinctrl_select_state(pctldev->p, state); pinctrl_select_state(pctldev->p, state);
pr_info("Select PWM Function\n"); printk("Select PWM Function\n");
for (i = 0; i < CTC_NUM_PWM; i++) { for (i = 0; i < CTC_NUM_PWM; i++) {
cur_value = ctc_pwm_readl(pc, i, CTC_CR_PWM); cur_value = ctc_pwm_readl(pc, i, CTC_CR_PWM);

View File

@ -1,4 +1,5 @@
/* rtc class driver for the SD2405 chip /*
* rtc class driver for the SD2405 chip
* *
* Author: Dale Farnsworth <dale@farnsworth.org> * Author: Dale Farnsworth <dale@farnsworth.org>
* *
@ -45,7 +46,7 @@
static struct i2c_driver sd2405_driver; static struct i2c_driver sd2405_driver;
static int sd2405_i2c_read_regs(struct i2c_client *client, u8 *buf) static int sd2405_i2c_read_regs(struct i2c_client *client, u8 * buf)
{ {
struct i2c_msg msgs[1] = { struct i2c_msg msgs[1] = {
{ {
@ -67,7 +68,9 @@ static int sd2405_i2c_read_regs(struct i2c_client *client, u8 *buf)
static int sd2405_i2c_write_regs(struct i2c_client *client, u8 const *buf) static int sd2405_i2c_write_regs(struct i2c_client *client, u8 const *buf)
{ {
int rc; int rc;
u8 temp_reg[SD2405_REG_LEN + 1] = { 0 }; u8 temp_reg[SD2405_REG_LEN + 1] = { 0 };
memcpy(&temp_reg[1], buf, SD2405_REG_LEN);
struct i2c_msg msgs[1] = { struct i2c_msg msgs[1] = {
{ {
@ -77,8 +80,6 @@ static int sd2405_i2c_write_regs(struct i2c_client *client, u8 const *buf)
.buf = temp_reg} .buf = temp_reg}
}; };
memcpy(&temp_reg[1], buf, SD2405_REG_LEN);
rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (rc != ARRAY_SIZE(msgs)) if (rc != ARRAY_SIZE(msgs))
goto write_failed; goto write_failed;
@ -114,7 +115,6 @@ static int sd2405_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
static int sd2405_i2c_set_write_protect(struct i2c_client *client) static int sd2405_i2c_set_write_protect(struct i2c_client *client)
{ {
int rc; int rc;
rc = i2c_smbus_write_byte_data(client, SD2405_REG_CTRL1, 0); rc = i2c_smbus_write_byte_data(client, SD2405_REG_CTRL1, 0);
rc += i2c_smbus_write_byte_data(client, SD2405_REG_CTRL2, 0); rc += i2c_smbus_write_byte_data(client, SD2405_REG_CTRL2, 0);
if (rc < 0) { if (rc < 0) {
@ -128,7 +128,6 @@ static int sd2405_i2c_set_write_protect(struct i2c_client *client)
static int sd2405_i2c_clear_write_protect(struct i2c_client *client) static int sd2405_i2c_clear_write_protect(struct i2c_client *client)
{ {
int rc; int rc;
rc = i2c_smbus_write_byte_data(client, SD2405_REG_CTRL2, rc = i2c_smbus_write_byte_data(client, SD2405_REG_CTRL2,
SD2405_REG_CONTROL1_WRITE); SD2405_REG_CONTROL1_WRITE);
rc += rc +=
@ -183,11 +182,12 @@ static int sd2405_rtc_set_time(struct device *dev, struct rtc_time *tm)
static int sd2405_remove(struct i2c_client *client) static int sd2405_remove(struct i2c_client *client)
{ {
#if 0
struct rtc_device *rtc = i2c_get_clientdata(client); struct rtc_device *rtc = i2c_get_clientdata(client);
if (rtc) if (rtc)
rtc_device_unregister(rtc); rtc_device_unregister(rtc);
#endif
return 0; return 0;
} }
@ -199,17 +199,20 @@ static const struct rtc_class_ops sd2405_rtc_ops = {
static int static int
sd2405_probe(struct i2c_client *client, const struct i2c_device_id *id) sd2405_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ {
int ret = 0;
struct rtc_device *rtc; struct rtc_device *rtc;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV; return -ENODEV;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
rtc = devm_rtc_allocate_device(&client->dev);
rtc = rtc_device_register(sd2405_driver.driver.name,
&client->dev, &sd2405_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) if (IS_ERR(rtc))
return PTR_ERR(rtc); return PTR_ERR(rtc);
rtc->ops = &sd2405_rtc_ops;
ret = rtc_register_device(rtc);
if (ret)
return ret;
i2c_set_clientdata(client, rtc); i2c_set_clientdata(client, rtc);

View File

@ -1,17 +1,9 @@
/* sdhci-ctc5236.c Support for SDHCI on Centec TsingMa SoC's /*
* sdhci-ctc5236.c Support for SDHCI on Centec TsingMa SoC's
* *
* Copyright (C) 2004-2017 Centec Networks (suzhou) Co., LTD. * Author: Wangyb <wangyb@centecnetworks.com>
* *
* Author: Jay Cao <caoj@centecnetworks.com> * Copyright 2005-2020, Centec Networks (Suzhou) Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
* *
*/ */
@ -27,38 +19,43 @@
#include <asm-generic/delay.h> #include <asm-generic/delay.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/raid/pq.h> #include <linux/raid/pq.h>
#include <linux/sizes.h>
#include <linux/dma-mapping.h>
#define REG_OFFSET_ADDR 0x500 #define REG_OFFSET_ADDR 0x500
#define MSHC_CTRL_R 0x8 #define MSHC_CTRL_R 0x8
#define AT_CTRL_R 0x40 #define AT_CTRL_R 0x40
#define SW_TUNE_EN 0x10 #define SW_TUNE_EN 0x10
#define SD_CLK_EN_MASK 0x00000001 #define SD_CLK_EN_MASK 0x00000001
#define AT_STAT_R 0x44 #define AT_STAT_R 0x44
#define MAX_TUNING_LOOP 0x80 #define MAX_TUNING_LOOP 0x80
#define MIN_TUNING_LOOP 0x0 #define MIN_TUNING_LOOP 0x0
#define TUNE_CTRL_STEP 1 #define TUNE_CTRL_STEP 1
#define EMMC_CTRL_R 0x2c
struct regmap *regmap_base;
#define SDHCI_REFCLK_150M 150000000 #define SDHCI_REFCLK_150M 150000000
static struct regmap *regmap_base;
static u32 version;
#define CTC_REV_TM_1_0 0x0
#define CTC_REV_TM_1_1 0x1
#define BOUNDARY_OK(addr, len) \
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
static u16 sdhci_ctc5236_readw(struct sdhci_host *host, int reg) static u16 sdhci_ctc5236_readw(struct sdhci_host *host, int reg)
{ {
if (unlikely(reg == SDHCI_HOST_VERSION)) if (unlikely(reg == SDHCI_HOST_VERSION)) {
return SDHCI_SPEC_300; return SDHCI_SPEC_300;
}
return readw(host->ioaddr + reg); return readw(host->ioaddr + reg);
} }
static u32 sdhci_ctc5236_readl(struct sdhci_host *host, int reg) static u32 sdhci_ctc5236_readl(struct sdhci_host *host, int reg)
{ {
u32 ret = readl(host->ioaddr + reg); u32 ret;
ret = readl(host->ioaddr + reg);
if (reg == SDHCI_CAPABILITIES_1)
ret &=
~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50);
if (reg == SDHCI_CAPABILITIES)
ret &= ~(SDHCI_CAN_64BIT);
return ret; return ret;
} }
@ -112,24 +109,74 @@ void sdhci_ctc5236_reset(struct sdhci_host *host, u8 mask)
} }
static void ctc5236_select_90degree_phase(struct sdhci_host *host)
{
u32 val = 0;
regmap_read(regmap_base, offsetof(struct SysCtl_regs, SysMshCfg), &val);
if (val & SYS_MSH_CFG_W0_MSH_INTF_C_CLK_TX_PHASE_SEL_MASK) {
val &= (~SYS_MSH_CFG_W0_MSH_INTF_C_CLK_TX_PHASE_SEL_MASK);
regmap_write(regmap_base,
offsetof(struct SysCtl_regs, SysMshCfg), val);
printk("select ctc 90 degree phase\n");
}
}
void ctc_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) void ctc_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
{ {
int val = 0; int val = 0;
if (clock == SDHCI_REFCLK_150M) { if (clock == SDHCI_REFCLK_150M) {
/* SDHCI reference clock change 150M */
regmap_read(regmap_base, regmap_read(regmap_base,
offsetof(struct SysCtl_regs, SysClkPeriCfg), &val); offsetof(struct SysCtl_regs, SysClkPeriCfg), &val);
val = val & (~SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK); if ((val & 0xc) == 0xc) {
val |= val =
((0x8 & SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK)) << 0; val &
regmap_write(regmap_base, (~SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK);
offsetof(struct SysCtl_regs, SysClkPeriCfg), val); val |=
((0x8 &
SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK)) <<
0;
regmap_write(regmap_base,
offsetof(struct SysCtl_regs,
SysClkPeriCfg), val);
printk("SDHCI reference clock change 150M\n");
}
}
if (version == CTC_REV_TM_1_1) {
if (host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) {
ctc5236_select_90degree_phase(host);
}
} }
sdhci_set_clock(host, clock); sdhci_set_clock(host, clock);
} }
/*
* If DMA addr spans 128MB boundary, we split the DMA transfer into two
* so that each DMA transfer doesn't exceed the boundary.
*/
static void sdhci_ctc5236_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len,
unsigned int cmd)
{
int tmplen, offset;
if (likely(!len || BOUNDARY_OK(addr, len))) {
sdhci_adma_write_desc(host, desc, addr, len, cmd);
return;
}
offset = addr & (SZ_128M - 1);
tmplen = SZ_128M - offset;
sdhci_adma_write_desc(host, desc, addr, tmplen, cmd);
addr += tmplen;
len -= tmplen;
sdhci_adma_write_desc(host, desc, addr, len, cmd);
}
static int sdhci_ctc5236_prepare_tuning(struct sdhci_host *host, static int sdhci_ctc5236_prepare_tuning(struct sdhci_host *host,
int CENTER_PH_CODE) int CENTER_PH_CODE)
{ {
@ -180,51 +227,54 @@ static int sdhci_ctc5236_execute_tuning(struct sdhci_host *host, u32 opcode)
val &= (~SYS_MSH_CFG_W0_MSH_INTF_RX_DLL_MASTER_BYPASS_MASK); val &= (~SYS_MSH_CFG_W0_MSH_INTF_RX_DLL_MASTER_BYPASS_MASK);
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysMshCfg), val); regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysMshCfg), val);
sdhci_ctc5236_prepare_tuning(host, 0);
/* find the mininum delay first which can pass tuning */ /* find the mininum delay first which can pass tuning */
min = MIN_TUNING_LOOP; min = MIN_TUNING_LOOP;
sdhci_ctc5236_prepare_tuning(host, min); sdhci_writel(host, min, REG_OFFSET_ADDR + AT_STAT_R);
while (min < MAX_TUNING_LOOP) { while (min < MAX_TUNING_LOOP) {
dev_dbg(mmc_dev(host->mmc), "#1# AT_STAT_R is %x\n",
sdhci_readl(host, REG_OFFSET_ADDR + AT_STAT_R));
if (!mmc_send_tuning(host->mmc, opcode, NULL)) if (!mmc_send_tuning(host->mmc, opcode, NULL))
break; break;
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
min += TUNE_CTRL_STEP; min += TUNE_CTRL_STEP;
sdhci_writel(host, min, REG_OFFSET_ADDR + AT_STAT_R); sdhci_writel(host, min, REG_OFFSET_ADDR + AT_STAT_R);
} }
/* find the maxinum delay which can not pass tuning */ /* find the maxinum delay which can not pass tuning */
max = min + TUNE_CTRL_STEP; max = min + TUNE_CTRL_STEP;
sdhci_ctc5236_prepare_tuning(host, max); sdhci_writel(host, max, REG_OFFSET_ADDR + AT_STAT_R);
while (max < MAX_TUNING_LOOP) { while (max < MAX_TUNING_LOOP) {
dev_dbg(mmc_dev(host->mmc), "#2# AT_STAT_R is %x\n",
sdhci_readl(host, REG_OFFSET_ADDR + AT_STAT_R));
if (mmc_send_tuning(host->mmc, opcode, NULL)) { if (mmc_send_tuning(host->mmc, opcode, NULL)) {
max -= TUNE_CTRL_STEP; max -= TUNE_CTRL_STEP;
break; break;
} }
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
max += TUNE_CTRL_STEP; max += TUNE_CTRL_STEP;
sdhci_writel(host, max, REG_OFFSET_ADDR + AT_STAT_R); sdhci_writel(host, max, REG_OFFSET_ADDR + AT_STAT_R);
} }
/* use average delay to get the best timing */ /* use average delay to get the best timing */
avg = (min + max) / 2; avg = (min + max) / 2;
sdhci_ctc5236_prepare_tuning(host, avg); sdhci_writel(host, avg, REG_OFFSET_ADDR + AT_STAT_R);
ret = mmc_send_tuning(host->mmc, opcode, NULL); ret = mmc_send_tuning(host->mmc, opcode, NULL);
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
sdhci_writel(host, avg, REG_OFFSET_ADDR + AT_STAT_R);
dev_info(mmc_dev(host->mmc), "Tuning %s at 0x%x ret %d\n", dev_info(mmc_dev(host->mmc),
ret ? "failed" : "passed", avg, ret); "Tuning %s at 0x%x ret %d, min is 0x%x, max is 0x%x\n",
ret ? "failed" : "passed", avg, ret, min, max);
return ret; return ret;
} }
static void sdhci_ctc5236_hw_reset(struct sdhci_host *host)
{
sdhci_writel(host, 0x0, REG_OFFSET_ADDR + EMMC_CTRL_R);
udelay(10);
sdhci_writel(host, 0xc, REG_OFFSET_ADDR + EMMC_CTRL_R);
udelay(300);
dev_info(mmc_dev(host->mmc), "Hardware reset\n");
}
static const struct sdhci_ops sdhci_ctc5236_ops = { static const struct sdhci_ops sdhci_ctc5236_ops = {
.read_w = sdhci_ctc5236_readw, .read_w = sdhci_ctc5236_readw,
.read_l = sdhci_ctc5236_readl, .read_l = sdhci_ctc5236_readl,
@ -234,12 +284,14 @@ static const struct sdhci_ops sdhci_ctc5236_ops = {
.set_uhs_signaling = sdhci_set_uhs_signaling, .set_uhs_signaling = sdhci_set_uhs_signaling,
.get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock,
.platform_execute_tuning = sdhci_ctc5236_execute_tuning, .platform_execute_tuning = sdhci_ctc5236_execute_tuning,
.adma_write_desc = sdhci_ctc5236_adma_write_desc,
.hw_reset = sdhci_ctc5236_hw_reset,
}; };
static struct sdhci_pltfm_data sdhci_ctc5236_pdata = { static struct sdhci_pltfm_data sdhci_ctc5236_pdata = {
.ops = &sdhci_ctc5236_ops, .ops = &sdhci_ctc5236_ops,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_BROKEN_HS200, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_BROKEN_HS200,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_BROKEN_ADMA, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
}; };
static int sdhci_ctc5236_probe(struct platform_device *pdev) static int sdhci_ctc5236_probe(struct platform_device *pdev)
@ -247,12 +299,40 @@ static int sdhci_ctc5236_probe(struct platform_device *pdev)
struct sdhci_host *host; struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host; struct sdhci_pltfm_host *pltfm_host;
struct clk *clk; struct clk *clk;
int ret; int ret, val;
u32 extra;
host = sdhci_pltfm_init(pdev, &sdhci_ctc5236_pdata, 0); host = sdhci_pltfm_init(pdev, &sdhci_ctc5236_pdata, 0);
if (IS_ERR(host)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);
/*
* extra adma table cnt for cross 128M boundary handling.
*/
extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M);
if (extra > SDHCI_MAX_SEGS)
extra = SDHCI_MAX_SEGS;
host->adma_table_cnt += extra;
regmap_base =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
if (IS_ERR(regmap_base))
return PTR_ERR(regmap_base);
val = 0x3400027;
regmap_write(regmap_base, offsetof(struct SysCtl_regs, SysMshCfg), val);
regmap_read(regmap_base, offsetof(struct SysCtl_regs, SysCtlSysRev),
&val);
version = (val == 0x1) ? CTC_REV_TM_1_1 : CTC_REV_TM_1_0;
mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask);
ret = mmc_of_parse(host->mmc);
if (ret)
goto err_sdhci_add;
clk = devm_clk_get(&pdev->dev, "mmc_clk"); clk = devm_clk_get(&pdev->dev, "mmc_clk");
if (IS_ERR(clk)) { if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Peripheral clk not found\n"); dev_err(&pdev->dev, "Peripheral clk not found\n");
@ -262,16 +342,17 @@ static int sdhci_ctc5236_probe(struct platform_device *pdev)
pltfm_host->clk = clk; pltfm_host->clk = clk;
clk_prepare_enable(clk); clk_prepare_enable(clk);
regmap_base = if (version == CTC_REV_TM_1_0) {
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl"); if (host->mmc->caps & MMC_CAP_1_8V_DDR) {
if (IS_ERR(regmap_base)) host->mmc->caps &= ~MMC_CAP_1_8V_DDR;
return PTR_ERR(regmap_base); printk("%s, not support DDR Mode\n", __func__);
}
}
mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask); if (host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR) {
host->mmc->caps2 &= ~MMC_CAP2_HS200_1_8V_SDR;
ret = mmc_of_parse(host->mmc); printk("%s, not support Hs200 Mode\n", __func__);
if (ret) }
goto err_sdhci_add;
ret = sdhci_add_host(host); ret = sdhci_add_host(host);
if (ret) if (ret)
@ -303,5 +384,5 @@ static struct platform_driver sdhci_ctc5236_driver = {
module_platform_driver(sdhci_ctc5236_driver); module_platform_driver(sdhci_ctc5236_driver);
MODULE_DESCRIPTION("SDHCI driver for Centec TsingMa SoCs"); MODULE_DESCRIPTION("SDHCI driver for Centec TsingMa SoCs");
MODULE_AUTHOR("Jay Cao <caoj@centecnetworks.com>"); MODULE_AUTHOR("Wangyb <wangyb@centecnetworks.com>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -1,11 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Copyright 2010 MontaVista Software, LLC. * Copyright 2010 MontaVista Software, LLC.
* *
* Author: Anton Vorontsov <avorontsov@ru.mvista.com> * Author: Anton Vorontsov <avorontsov@ru.mvista.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 _DRIVERS_MMC_SDHCI_PLTFM_H #ifndef _DRIVERS_MMC_SDHCI_PLTFM_H
@ -28,7 +25,7 @@ struct sdhci_pltfm_host {
unsigned int clock; unsigned int clock;
u16 xfer_mode_shadow; u16 xfer_mode_shadow;
unsigned long private[0] ____cacheline_aligned; unsigned long private[] ____cacheline_aligned;
}; };
#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER #ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
@ -86,15 +83,20 @@ static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
int base = reg & ~0x3; int base = reg & ~0x3;
int shift = (reg & 0x3) * 8; int shift = (reg & 0x3) * 8;
clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift); clrsetbits_be32(host->ioaddr + base, 0xff << shift, val << shift);
} }
#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */ #endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */
extern void sdhci_get_of_property(struct platform_device *pdev); void sdhci_get_property(struct platform_device *pdev);
static inline void sdhci_get_of_property(struct platform_device *pdev)
{
return sdhci_get_property(pdev);
}
extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata, const struct sdhci_pltfm_data *pdata,
size_t priv_size); size_t priv_size);
extern void sdhci_pltfm_free(struct platform_device *pdev); extern void sdhci_pltfm_free(struct platform_device *pdev);
extern int sdhci_pltfm_register(struct platform_device *pdev, extern int sdhci_pltfm_register(struct platform_device *pdev,
@ -109,8 +111,20 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
return host->private; return host->private;
} }
extern const struct dev_pm_ops sdhci_pltfm_pmops;
#ifdef CONFIG_PM_SLEEP
int sdhci_pltfm_suspend(struct device *dev); int sdhci_pltfm_suspend(struct device *dev);
int sdhci_pltfm_resume(struct device *dev); int sdhci_pltfm_resume(struct device *dev);
extern const struct dev_pm_ops sdhci_pltfm_pmops; #else
static inline int sdhci_pltfm_suspend(struct device *dev)
{
return 0;
}
static inline int sdhci_pltfm_resume(struct device *dev)
{
return 0;
}
#endif
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */ #endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */

View File

@ -1,18 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* /*
* linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
* *
* Header file for Host Controller registers and I/O accessors. * Header file for Host Controller registers and I/O accessors.
* *
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/ */
#ifndef __SDHCI_HW_H #ifndef __SDHCI_HW_H
#define __SDHCI_HW_H #define __SDHCI_HW_H
#include <linux/bits.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/types.h> #include <linux/types.h>
@ -28,6 +25,7 @@
#define SDHCI_DMA_ADDRESS 0x00 #define SDHCI_DMA_ADDRESS 0x00
#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS #define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
#define SDHCI_32BIT_BLK_CNT SDHCI_DMA_ADDRESS
#define SDHCI_BLOCK_SIZE 0x04 #define SDHCI_BLOCK_SIZE 0x04
#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
@ -41,6 +39,7 @@
#define SDHCI_TRNS_BLK_CNT_EN 0x02 #define SDHCI_TRNS_BLK_CNT_EN 0x02
#define SDHCI_TRNS_AUTO_CMD12 0x04 #define SDHCI_TRNS_AUTO_CMD12 0x04
#define SDHCI_TRNS_AUTO_CMD23 0x08 #define SDHCI_TRNS_AUTO_CMD23 0x08
#define SDHCI_TRNS_AUTO_SEL 0x0C
#define SDHCI_TRNS_READ 0x10 #define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20 #define SDHCI_TRNS_MULTI 0x20
@ -71,6 +70,10 @@
#define SDHCI_SPACE_AVAILABLE 0x00000400 #define SDHCI_SPACE_AVAILABLE 0x00000400
#define SDHCI_DATA_AVAILABLE 0x00000800 #define SDHCI_DATA_AVAILABLE 0x00000800
#define SDHCI_CARD_PRESENT 0x00010000 #define SDHCI_CARD_PRESENT 0x00010000
#define SDHCI_CARD_PRES_SHIFT 16
#define SDHCI_CD_STABLE 0x00020000
#define SDHCI_CD_LVL 0x00040000
#define SDHCI_CD_LVL_SHIFT 18
#define SDHCI_WRITE_PROTECT 0x00080000 #define SDHCI_WRITE_PROTECT 0x00080000
#define SDHCI_DATA_LVL_MASK 0x00F00000 #define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20 #define SDHCI_DATA_LVL_SHIFT 20
@ -86,7 +89,8 @@
#define SDHCI_CTRL_ADMA1 0x08 #define SDHCI_CTRL_ADMA1 0x08
#define SDHCI_CTRL_ADMA32 0x10 #define SDHCI_CTRL_ADMA32 0x10
#define SDHCI_CTRL_ADMA64 0x18 #define SDHCI_CTRL_ADMA64 0x18
#define SDHCI_CTRL_8BITBUS 0x20 #define SDHCI_CTRL_ADMA3 0x18
#define SDHCI_CTRL_8BITBUS 0x20
#define SDHCI_CTRL_CDTEST_INS 0x40 #define SDHCI_CTRL_CDTEST_INS 0x40
#define SDHCI_CTRL_CDTEST_EN 0x80 #define SDHCI_CTRL_CDTEST_EN 0x80
@ -111,6 +115,7 @@
#define SDHCI_DIV_HI_MASK 0x300 #define SDHCI_DIV_HI_MASK 0x300
#define SDHCI_PROG_CLOCK_MODE 0x0020 #define SDHCI_PROG_CLOCK_MODE 0x0020
#define SDHCI_CLOCK_CARD_EN 0x0004 #define SDHCI_CLOCK_CARD_EN 0x0004
#define SDHCI_CLOCK_PLL_EN 0x0008
#define SDHCI_CLOCK_INT_STABLE 0x0002 #define SDHCI_CLOCK_INT_STABLE 0x0002
#define SDHCI_CLOCK_INT_EN 0x0001 #define SDHCI_CLOCK_INT_EN 0x0001
@ -180,7 +185,7 @@
#define SDHCI_CTRL_UHS_SDR50 0x0002 #define SDHCI_CTRL_UHS_SDR50 0x0002
#define SDHCI_CTRL_UHS_SDR104 0x0003 #define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004 #define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */ #define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
#define SDHCI_CTRL_VDD_180 0x0008 #define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000 #define SDHCI_CTRL_DRV_TYPE_B 0x0000
@ -189,15 +194,16 @@
#define SDHCI_CTRL_DRV_TYPE_D 0x0030 #define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040 #define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080 #define SDHCI_CTRL_TUNED_CLK 0x0080
#define SDHCI_CMD23_ENABLE 0x0800
#define SDHCI_CTRL_V4_MODE 0x1000
#define SDHCI_CTRL_64BIT_ADDR 0x2000
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000 #define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40 #define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F #define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0)
#define SDHCI_TIMEOUT_CLK_SHIFT 0
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 #define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
#define SDHCI_CLOCK_BASE_MASK 0x00003F00 #define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8)
#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00 #define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8)
#define SDHCI_CLOCK_BASE_SHIFT 8
#define SDHCI_MAX_BLOCK_MASK 0x00030000 #define SDHCI_MAX_BLOCK_MASK 0x00030000
#define SDHCI_MAX_BLOCK_SHIFT 16 #define SDHCI_MAX_BLOCK_SHIFT 16
#define SDHCI_CAN_DO_8BIT 0x00040000 #define SDHCI_CAN_DO_8BIT 0x00040000
@ -209,33 +215,28 @@
#define SDHCI_CAN_VDD_330 0x01000000 #define SDHCI_CAN_VDD_330 0x01000000
#define SDHCI_CAN_VDD_300 0x02000000 #define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000 #define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT_V4 0x08000000
#define SDHCI_CAN_64BIT 0x10000000 #define SDHCI_CAN_64BIT 0x10000000
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_SUPPORT_SDR50 0x00000001 #define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002 #define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004 #define SDHCI_SUPPORT_DDR50 0x00000004
#define SDHCI_DRIVER_TYPE_A 0x00000010 #define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020 #define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040 #define SDHCI_DRIVER_TYPE_D 0x00000040
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00 #define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8)
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
#define SDHCI_USE_SDR50_TUNING 0x00002000 #define SDHCI_USE_SDR50_TUNING 0x00002000
#define SDHCI_RETUNING_MODE_MASK 0x0000C000 #define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
#define SDHCI_RETUNING_MODE_SHIFT 14 #define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000 #define SDHCI_CAN_DO_ADMA3 0x08000000
#define SDHCI_CLOCK_MUL_SHIFT 16 #define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_MAX_CURRENT 0x48 #define SDHCI_MAX_CURRENT 0x48
#define SDHCI_MAX_CURRENT_LIMIT 0xFF #define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF #define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_330_SHIFT 0 #define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00 #define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
#define SDHCI_MAX_CURRENT_300_SHIFT 8
#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
#define SDHCI_MAX_CURRENT_180_SHIFT 16
#define SDHCI_MAX_CURRENT_MULTIPLIER 4 #define SDHCI_MAX_CURRENT_MULTIPLIER 4
/* 4C-4F reserved for more max current */ /* 4C-4F reserved for more max current */
@ -252,18 +253,16 @@
/* 60-FB reserved */ /* 60-FB reserved */
#define SDHCI_PRESET_FOR_HIGH_SPEED 0x64
#define SDHCI_PRESET_FOR_SDR12 0x66 #define SDHCI_PRESET_FOR_SDR12 0x66
#define SDHCI_PRESET_FOR_SDR25 0x68 #define SDHCI_PRESET_FOR_SDR25 0x68
#define SDHCI_PRESET_FOR_SDR50 0x6A #define SDHCI_PRESET_FOR_SDR50 0x6A
#define SDHCI_PRESET_FOR_SDR104 0x6C #define SDHCI_PRESET_FOR_SDR104 0x6C
#define SDHCI_PRESET_FOR_DDR50 0x6E #define SDHCI_PRESET_FOR_DDR50 0x6E
#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ #define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
#define SDHCI_PRESET_DRV_MASK 0xC000 #define SDHCI_PRESET_DRV_MASK GENMASK(15, 14)
#define SDHCI_PRESET_DRV_SHIFT 14 #define SDHCI_PRESET_CLKGEN_SEL BIT(10)
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400 #define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
#define SDHCI_PRESET_CLKGEN_SEL_SHIFT 10
#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF
#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0
#define SDHCI_SLOT_INT_STATUS 0xFC #define SDHCI_SLOT_INT_STATUS 0xFC
@ -275,6 +274,9 @@
#define SDHCI_SPEC_100 0 #define SDHCI_SPEC_100 0
#define SDHCI_SPEC_200 1 #define SDHCI_SPEC_200 1
#define SDHCI_SPEC_300 2 #define SDHCI_SPEC_300 2
#define SDHCI_SPEC_400 3
#define SDHCI_SPEC_410 4
#define SDHCI_SPEC_420 5
/* /*
* End of controller registers. * End of controller registers.
@ -294,10 +296,10 @@
/* ADMA2 32-bit descriptor */ /* ADMA2 32-bit descriptor */
struct sdhci_adma2_32_desc { struct sdhci_adma2_32_desc {
__le16 cmd; __le16 cmd;
__le16 len; __le16 len;
__le32 addr; __le32 addr;
} __packed __aligned(4); } __packed __aligned(4);
/* ADMA2 data alignment */ /* ADMA2 data alignment */
#define SDHCI_ADMA2_ALIGN 4 #define SDHCI_ADMA2_ALIGN 4
@ -310,19 +312,25 @@ struct sdhci_adma2_32_desc {
*/ */
#define SDHCI_ADMA2_DESC_ALIGN 8 #define SDHCI_ADMA2_DESC_ALIGN 8
/* ADMA2 64-bit DMA descriptor size */ /*
#define SDHCI_ADMA2_64_DESC_SZ 12 * ADMA2 64-bit DMA descriptor size
* According to SD Host Controller spec v4.10, there are two kinds of
* descriptors for 64-bit addressing mode: 96-bit Descriptor and 128-bit
* Descriptor, if Host Version 4 Enable is set in the Host Control 2
* register, 128-bit Descriptor will be selected.
*/
#define SDHCI_ADMA2_64_DESC_SZ(host) ((host)->v4_mode ? 16 : 12)
/* /*
* ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
* aligned. * aligned.
*/ */
struct sdhci_adma2_64_desc { struct sdhci_adma2_64_desc {
__le16 cmd; __le16 cmd;
__le16 len; __le16 len;
__le32 addr_lo; __le32 addr_lo;
__le32 addr_hi; __le32 addr_hi;
} __packed __aligned(4); } __packed __aligned(4);
#define ADMA2_TRAN_VALID 0x21 #define ADMA2_TRAN_VALID 0x21
#define ADMA2_NOP_END_VALID 0x3 #define ADMA2_NOP_END_VALID 0x3
@ -343,7 +351,7 @@ struct sdhci_adma2_64_desc {
* command and response, and the time between response and start of data is * command and response, and the time between response and start of data is
* not known, set the command transfer time to 10ms. * not known, set the command transfer time to 10ms.
*/ */
#define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */ #define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */
enum sdhci_cookie { enum sdhci_cookie {
COOKIE_UNMAPPED, COOKIE_UNMAPPED,
@ -391,8 +399,12 @@ struct sdhci_host {
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) #define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
/* Controller reports inverted write-protect state */ /* Controller reports inverted write-protect state */
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) #define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
/* Controller has unusable command queue engine */
#define SDHCI_QUIRK_BROKEN_CQE (1<<17)
/* Controller does not like fast PIO transfers */ /* Controller does not like fast PIO transfers */
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) #define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
/* Controller does not have a LED */
#define SDHCI_QUIRK_NO_LED (1<<19)
/* Controller has to be forced to use block size of 2048 bytes */ /* Controller has to be forced to use block size of 2048 bytes */
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) #define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
/* Controller cannot do multi-block transfers */ /* Controller cannot do multi-block transfers */
@ -455,9 +467,17 @@ struct sdhci_host {
* obtainable timeout. * obtainable timeout.
*/ */
#define SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1<<17) #define SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1<<17)
/*
* 32-bit block count may not support eMMC where upper bits of CMD23 are used
* for other purposes. Consequently we support 16-bit block count by default.
* Otherwise, SDHCI_QUIRK2_USE_32BIT_BLK_CNT can be selected to use 32-bit
* block count.
*/
#define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18)
int irq; /* Device IRQ */ int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */ void __iomem *ioaddr; /* Mapped address */
phys_addr_t mapbase; /* physical address base */
char *bounce_buffer; /* For packing SDMA reads/writes */ char *bounce_buffer; /* For packing SDMA reads/writes */
dma_addr_t bounce_addr; dma_addr_t bounce_addr;
unsigned int bounce_buffer_size; unsigned int bounce_buffer_size;
@ -485,7 +505,6 @@ struct sdhci_host {
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ #define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ #define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */ #define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */ #define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */ #define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */ #define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */
@ -506,10 +525,14 @@ struct sdhci_host {
bool preset_enabled; /* Preset is enabled */ bool preset_enabled; /* Preset is enabled */
bool pending_reset; /* Cmd/data reset is pending */ bool pending_reset; /* Cmd/data reset is pending */
bool irq_wake_enabled; /* IRQ wakeup is enabled */ bool irq_wake_enabled; /* IRQ wakeup is enabled */
bool v4_mode; /* Host Version 4 Enable */
bool use_external_dma; /* Host selects to use external DMA */
bool always_defer_done; /* Always defer to complete requests */
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */ struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */
struct mmc_command *cmd; /* Current command */ struct mmc_command *cmd; /* Current command */
struct mmc_command *data_cmd; /* Current data command */ struct mmc_command *data_cmd; /* Current data command */
struct mmc_command *deferred_cmd; /* Deferred command */
struct mmc_data *data; /* Current data request */ struct mmc_data *data; /* Current data request */
unsigned int data_early:1; /* Data finished before cmd */ unsigned int data_early:1; /* Data finished before cmd */
@ -527,87 +550,109 @@ struct sdhci_host {
dma_addr_t adma_addr; /* Mapped ADMA descr. table */ dma_addr_t adma_addr; /* Mapped ADMA descr. table */
dma_addr_t align_addr; /* Mapped bounce buffer */ dma_addr_t align_addr; /* Mapped bounce buffer */
unsigned int desc_sz; /* ADMA descriptor size */ unsigned int desc_sz; /* ADMA current descriptor size */
unsigned int alloc_desc_sz; /* ADMA descr. max size host supports */
struct tasklet_struct finish_tasklet; /* Tasklet structures */ struct workqueue_struct *complete_wq; /* Request completion wq */
struct work_struct complete_work; /* Request completion work */
struct timer_list timer; /* Timer for timeouts */ struct timer_list timer; /* Timer for timeouts */
struct timer_list data_timer; /* Timer for data timeouts */ struct timer_list data_timer; /* Timer for data timeouts */
#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
struct dma_chan *rx_chan;
struct dma_chan *tx_chan;
#endif
u32 caps; /* CAPABILITY_0 */ u32 caps; /* CAPABILITY_0 */
u32 caps1; /* CAPABILITY_1 */ u32 caps1; /* CAPABILITY_1 */
bool read_caps; /* Capability flags have been read */ bool read_caps; /* Capability flags have been read */
unsigned int ocr_avail_sdio; /* OCR bit masks */ bool sdhci_core_to_disable_vqmmc; /* sdhci core can disable vqmmc */
unsigned int ocr_avail_sd; unsigned int ocr_avail_sdio; /* OCR bit masks */
unsigned int ocr_avail_mmc; unsigned int ocr_avail_sd;
unsigned int ocr_avail_mmc;
u32 ocr_mask; /* available voltages */ u32 ocr_mask; /* available voltages */
unsigned timing; /* Current timing */ unsigned timing; /* Current timing */
u32 thread_isr; u32 thread_isr;
/* cached registers */ /* cached registers */
u32 ier; u32 ier;
bool cqe_on; /* CQE is operating */ bool cqe_on; /* CQE is operating */
u32 cqe_ier; /* CQE interrupt mask */ u32 cqe_ier; /* CQE interrupt mask */
u32 cqe_err_ier; /* CQE error interrupt mask */ u32 cqe_err_ier; /* CQE error interrupt mask */
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
unsigned int tuning_count; /* Timer count for re-tuning */ unsigned int tuning_count; /* Timer count for re-tuning */
unsigned int tuning_mode; /* Re-tuning mode supported by host */ unsigned int tuning_mode; /* Re-tuning mode supported by host */
unsigned int tuning_err; /* Error code for re-tuning */
#define SDHCI_TUNING_MODE_1 0 #define SDHCI_TUNING_MODE_1 0
#define SDHCI_TUNING_MODE_2 1 #define SDHCI_TUNING_MODE_2 1
#define SDHCI_TUNING_MODE_3 2 #define SDHCI_TUNING_MODE_3 2
/* Delay (ms) between tuning commands */ /* Delay (ms) between tuning commands */
int tuning_delay; int tuning_delay;
int tuning_loop_count;
/* Host SDMA buffer boundary. */ /* Host SDMA buffer boundary. */
u32 sdma_boundary; u32 sdma_boundary;
u64 data_timeout; /* Host ADMA table count */
u32 adma_table_cnt;
unsigned long private[0] ____cacheline_aligned; u64 data_timeout;
unsigned long private[] ____cacheline_aligned;
}; };
struct sdhci_ops { struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*read_l)(struct sdhci_host *host, int reg); u32(*read_l) (struct sdhci_host * host, int reg);
u16 (*read_w)(struct sdhci_host *host, int reg); u16(*read_w) (struct sdhci_host * host, int reg);
u8 (*read_b)(struct sdhci_host *host, int reg); u8(*read_b) (struct sdhci_host * host, int reg);
void (*write_l)(struct sdhci_host *host, u32 val, int reg); void (*write_l) (struct sdhci_host * host, u32 val, int reg);
void (*write_w)(struct sdhci_host *host, u16 val, int reg); void (*write_w) (struct sdhci_host * host, u16 val, int reg);
void (*write_b)(struct sdhci_host *host, u8 val, int reg); void (*write_b) (struct sdhci_host * host, u8 val, int reg);
#endif #endif
void (*set_clock)(struct sdhci_host *host, unsigned int clock); void (*set_clock) (struct sdhci_host * host, unsigned int clock);
void (*set_power)(struct sdhci_host *host, unsigned char mode, void (*set_power) (struct sdhci_host * host, unsigned char mode,
unsigned short vdd); unsigned short vdd);
u32 (*irq)(struct sdhci_host *host, u32 intmask); u32(*irq) (struct sdhci_host * host, u32 intmask);
int (*enable_dma)(struct sdhci_host *host); int (*set_dma_mask) (struct sdhci_host * host);
unsigned int (*get_max_clock)(struct sdhci_host *host); int (*enable_dma) (struct sdhci_host * host);
unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_max_clock) (struct sdhci_host * host);
unsigned int (*get_min_clock) (struct sdhci_host * host);
/* get_timeout_clock should return clk rate in unit of Hz */ /* get_timeout_clock should return clk rate in unit of Hz */
unsigned int (*get_timeout_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock) (struct sdhci_host * host);
unsigned int (*get_max_timeout_count)(struct sdhci_host *host); unsigned int (*get_max_timeout_count) (struct sdhci_host * host);
void (*set_timeout)(struct sdhci_host *host, void (*set_timeout) (struct sdhci_host * host,
struct mmc_command *cmd); struct mmc_command * cmd);
void (*set_bus_width)(struct sdhci_host *host, int width); void (*set_bus_width) (struct sdhci_host * host, int width);
void (*platform_send_init_74_clocks)(struct sdhci_host *host, void (*platform_send_init_74_clocks) (struct sdhci_host * host,
u8 power_mode); u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host); unsigned int (*get_ro) (struct sdhci_host * host);
void (*reset)(struct sdhci_host *host, u8 mask); void (*reset) (struct sdhci_host * host, u8 mask);
int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode); int (*platform_execute_tuning) (struct sdhci_host * host, u32 opcode);
void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); void (*set_uhs_signaling) (struct sdhci_host * host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host); void (*hw_reset) (struct sdhci_host * host);
void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*adma_workaround) (struct sdhci_host * host, u32 intmask);
void (*card_event)(struct sdhci_host *host); void (*card_event) (struct sdhci_host * host);
void (*voltage_switch)(struct sdhci_host *host); void (*voltage_switch) (struct sdhci_host * host);
void (*adma_write_desc) (struct sdhci_host * host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
void (*copy_to_bounce_buffer) (struct sdhci_host * host,
struct mmc_data * data,
unsigned int length);
void (*request_done) (struct sdhci_host * host,
struct mmc_request * mrq);
void (*dump_vendor_regs) (struct sdhci_host * host);
}; };
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@ -703,33 +748,31 @@ static inline void *sdhci_priv(struct sdhci_host *host)
} }
void sdhci_card_detect(struct sdhci_host *host); void sdhci_card_detect(struct sdhci_host *host);
void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, void __sdhci_read_caps(struct sdhci_host *host, const u16 * ver,
u32 *caps1); const u32 * caps, const u32 * caps1);
int sdhci_setup_host(struct sdhci_host *host); int sdhci_setup_host(struct sdhci_host *host);
void sdhci_cleanup_host(struct sdhci_host *host); void sdhci_cleanup_host(struct sdhci_host *host);
int __sdhci_add_host(struct sdhci_host *host); int __sdhci_add_host(struct sdhci_host *host);
int sdhci_add_host(struct sdhci_host *host); int sdhci_add_host(struct sdhci_host *host);
void sdhci_remove_host(struct sdhci_host *host, int dead); void sdhci_remove_host(struct sdhci_host *host, int dead);
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
static inline void sdhci_read_caps(struct sdhci_host *host) static inline void sdhci_read_caps(struct sdhci_host *host)
{ {
__sdhci_read_caps(host, NULL, NULL, NULL); __sdhci_read_caps(host, NULL, NULL, NULL);
} }
static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host)
{
return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
}
u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock, u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock,
unsigned int *actual_clock); unsigned int *actual_clock);
void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
void sdhci_enable_clk(struct sdhci_host *host, u16 clk); void sdhci_enable_clk(struct sdhci_host *host, u16 clk);
void sdhci_set_power(struct sdhci_host *host, unsigned char mode, void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
unsigned short vdd); unsigned short vdd);
void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
unsigned char mode, unsigned short vdd);
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd); unsigned short vdd);
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
void sdhci_set_bus_width(struct sdhci_host *host, int width); void sdhci_set_bus_width(struct sdhci_host *host, int width);
void sdhci_reset(struct sdhci_host *host, u8 mask); void sdhci_reset(struct sdhci_host *host, u8 mask);
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing); void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
@ -738,12 +781,14 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios); struct mmc_ios *ios);
void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable); void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable);
void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
#ifdef CONFIG_PM #ifdef CONFIG_PM
int sdhci_suspend_host(struct sdhci_host *host); int sdhci_suspend_host(struct sdhci_host *host);
int sdhci_resume_host(struct sdhci_host *host); int sdhci_resume_host(struct sdhci_host *host);
int sdhci_runtime_suspend_host(struct sdhci_host *host); int sdhci_runtime_suspend_host(struct sdhci_host *host);
int sdhci_runtime_resume_host(struct sdhci_host *host); int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset);
#endif #endif
void sdhci_cqe_enable(struct mmc_host *mmc); void sdhci_cqe_enable(struct mmc_host *mmc);
@ -752,10 +797,15 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
int *data_error); int *data_error);
void sdhci_dumpregs(struct sdhci_host *host); void sdhci_dumpregs(struct sdhci_host *host);
void sdhci_enable_v4_mode(struct sdhci_host *host);
void sdhci_start_tuning(struct sdhci_host *host); void sdhci_start_tuning(struct sdhci_host *host);
void sdhci_end_tuning(struct sdhci_host *host); void sdhci_end_tuning(struct sdhci_host *host);
void sdhci_reset_tuning(struct sdhci_host *host); void sdhci_reset_tuning(struct sdhci_host *host);
void sdhci_send_tuning(struct sdhci_host *host, u32 opcode); void sdhci_send_tuning(struct sdhci_host *host, u32 opcode);
void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode);
void sdhci_switch_external_dma(struct sdhci_host *host, bool en);
void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable);
void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
#endif /* __SDHCI_HW_H */ #endif /* __SDHCI_HW_H */

View File

@ -111,7 +111,7 @@ enum type_mode {
TYPE_MAX TYPE_MAX
}; };
static int ctc_reg_read(struct ctc_qspi *ctc_qspi, u32 reg, u32 *value) static int ctc_reg_read(struct ctc_qspi *ctc_qspi, u32 reg, u32 * value)
{ {
*value = readl(ctc_qspi->regs + reg); *value = readl(ctc_qspi->regs + reg);
return *value; return *value;
@ -169,12 +169,12 @@ static noinline int ctc_write_tx_buf(struct ctc_qspi *ctc_qspi, u8 offset,
return 0; return 0;
} }
static noinline int check_buf_ok(u8 *buf, int i) static noinline int check_buf_ok(u8 * buf, int i)
{ {
return buf && (buf + i); return buf && (buf + i);
} }
static noinline int fill_tx_entry(struct ctc_qspi *ctc_qspi, u8 *buf, int i, static noinline int fill_tx_entry(struct ctc_qspi *ctc_qspi, u8 * buf, int i,
u8 off) u8 off)
{ {
ctc_qspi->tx_entry |= buf[i] << (off % 4) * 8; ctc_qspi->tx_entry |= buf[i] << (off % 4) * 8;
@ -182,12 +182,12 @@ static noinline int fill_tx_entry(struct ctc_qspi *ctc_qspi, u8 *buf, int i,
return 0; return 0;
} }
static noinline void update_offset(u8 *offset, u8 off) static noinline void update_offset(u8 * offset, u8 off)
{ {
*offset = off; *offset = off;
} }
static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 *offset, u8 *buf, static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 * offset, u8 * buf,
u32 len) u32 len)
{ {
@ -195,8 +195,9 @@ static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 *offset, u8 *buf,
u8 off = *offset; u8 off = *offset;
while (i < len) { while (i < len) {
if (check_buf_ok(buf, i)) if (check_buf_ok(buf, i)) {
fill_tx_entry(ctc_qspi, buf, i, off); fill_tx_entry(ctc_qspi, buf, i, off);
}
if (off % 4 == 0) { if (off % 4 == 0) {
ctc_write_tx_buf(ctc_qspi, off, ctc_qspi->tx_entry); ctc_write_tx_buf(ctc_qspi, off, ctc_qspi->tx_entry);
@ -210,7 +211,7 @@ static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 *offset, u8 *buf,
} }
static void ctc_fill_pp_buf(struct ctc_qspi *ctc_qspi, u32 *offset, u8 *buf, static void ctc_fill_pp_buf(struct ctc_qspi *ctc_qspi, u32 * offset, u8 * buf,
u32 len) u32 len)
{ {
u32 i = 0, j = 0; u32 i = 0, j = 0;
@ -218,8 +219,9 @@ static void ctc_fill_pp_buf(struct ctc_qspi *ctc_qspi, u32 *offset, u8 *buf,
while (i < len) { while (i < len) {
for (j = 0; j < 4; j++) { for (j = 0; j < 4; j++) {
if (buf && (buf + i)) if (buf && (buf + i)) {
ctc_qspi->tx_entry |= buf[i + j] << (j % 4) * 8; ctc_qspi->tx_entry |= buf[i + j] << (j % 4) * 8;
}
} }
ctc_write_tx_buf(ctc_qspi, off, ctc_qspi->tx_entry); ctc_write_tx_buf(ctc_qspi, off, ctc_qspi->tx_entry);
ctc_qspi->tx_entry = 0; ctc_qspi->tx_entry = 0;
@ -303,15 +305,17 @@ static void ctc_qspi_pio_ctrl(struct ctc_qspi *ctc_qspi)
{ {
u32 ctrl = 0; u32 ctrl = 0;
ctrl = CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) | ctrl =
CTRL_PRE_CYCLE(ctc_qspi->precycle) | CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) | CTRL_PRE_CYCLE(ctc_qspi->
CTRL_POST_CYCLE(ctc_qspi->postcycle) | precycle)
CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef) | | CTRL_POST_CYCLE(ctc_qspi->
CTRL_SOUT3_DEFAULT(ctc_qspi->sout3def) | postcycle) | CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef)
CTRL_SOUT2_DEFAULT(ctc_qspi->sout2def) | | CTRL_SOUT3_DEFAULT(ctc_qspi->
CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) | sout3def) | CTRL_SOUT2_DEFAULT(ctc_qspi->
CTRL_CS(ctc_qspi->cs_select) | sout2def)
CTRL_DIV_SCLK(ctc_qspi->clkdiv); | CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) | CTRL_CS(ctc_qspi->
cs_select)
| CTRL_DIV_SCLK(ctc_qspi->clkdiv);
ctc_reg_write_mask(ctc_qspi, PIO_CTRL(ctc_qspi->qspi_mode), ctrl, ctc_reg_write_mask(ctc_qspi, PIO_CTRL(ctc_qspi->qspi_mode), ctrl,
0xffffffff); 0xffffffff);
@ -321,15 +325,17 @@ static void ctc_qspi_pp_ctrl(struct ctc_qspi *ctc_qspi)
{ {
u32 ctrl = 0; u32 ctrl = 0;
ctrl = CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) | ctrl =
CTRL_PRE_CYCLE(ctc_qspi->precycle) | CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) | CTRL_PRE_CYCLE(ctc_qspi->
CTRL_POST_CYCLE(ctc_qspi->postcycle) | precycle)
CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef) | | CTRL_POST_CYCLE(ctc_qspi->
CTRL_SOUT3_DEFAULT(ctc_qspi->sout3def) | postcycle) | CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef)
CTRL_SOUT2_DEFAULT(ctc_qspi->sout2def) | | CTRL_SOUT3_DEFAULT(ctc_qspi->
CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) | sout3def) | CTRL_SOUT2_DEFAULT(ctc_qspi->
CTRL_CS(ctc_qspi->cs_select) | sout2def)
CTRL_DIV_SCLK(ctc_qspi->clkdiv); | CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) | CTRL_CS(ctc_qspi->
cs_select)
| CTRL_DIV_SCLK(ctc_qspi->clkdiv);
ctc_reg_write_mask(ctc_qspi, PP_CTRL, ctrl, 0xffffffff); ctc_reg_write_mask(ctc_qspi, PP_CTRL, ctrl, 0xffffffff);
} }
@ -343,18 +349,17 @@ static u32 ctc_pp_conf(u8 lanes, u32 len)
return (lanes << 16) | (cycle); return (lanes << 16) | (cycle);
} }
static int ctc_read_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 *value) static int ctc_read_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 * value)
{ {
*value = readb(ctc_qspi->regs + CTC_QSPI_RX_BUFF + offset); *value = readb(ctc_qspi->regs + CTC_QSPI_RX_BUFF + offset);
return 0; return 0;
} }
static void ctc_extra_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 *buf, static void ctc_extra_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 * buf,
u8 len) u8 len)
{ {
int i = 0; int i = 0;
while (i < len) { while (i < len) {
ctc_read_rx_buf(ctc_qspi, offset, &buf[i++]); ctc_read_rx_buf(ctc_qspi, offset, &buf[i++]);
offset--; offset--;
@ -523,11 +528,13 @@ int ctc_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{ {
//max data transfer size = tx buffer size - (cmd - addr -dummy ) //max data transfer size = tx buffer size - (cmd - addr -dummy )
if (op->data.dir == SPI_MEM_DATA_IN) { if (op->data.dir == SPI_MEM_DATA_IN) {
if (op->data.nbytes > CTC_QSPI_RX_BUFFER_SIZE - 6) if (op->data.nbytes > CTC_QSPI_RX_BUFFER_SIZE - 6) {
op->data.nbytes = CTC_QSPI_RX_BUFFER_SIZE - 6; op->data.nbytes = CTC_QSPI_RX_BUFFER_SIZE - 6;
}
} else { } else {
if (op->data.nbytes > CTC_QSPI_TX_BUFFER_SIZE) if (op->data.nbytes > CTC_QSPI_TX_BUFFER_SIZE) {
op->data.nbytes = CTC_QSPI_TX_BUFFER_SIZE; op->data.nbytes = CTC_QSPI_TX_BUFFER_SIZE;
}
} }
return 0; return 0;
@ -558,7 +565,7 @@ static int ctc_qspi_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
master->mode_bits = master->mode_bits =
SPI_MODE_3 | SPI_MODE_1 | SPI_TX_DUAL | SPI_RX_DUAL | SPI_TX_QUAD | SPI_MODE_3 | SPI_MODE_0 | SPI_TX_DUAL | SPI_RX_DUAL | SPI_TX_QUAD |
SPI_RX_QUAD; SPI_RX_QUAD;
master->setup = ctc_qspi_setup; master->setup = ctc_qspi_setup;
master->transfer_one_message = ctc_qspi_start_transfer_one; master->transfer_one_message = ctc_qspi_start_transfer_one;

View File

@ -1176,7 +1176,12 @@ dal_alloc_dma_pool(int lchip, int size)
if (use_high_memory) if (use_high_memory)
{ {
dma_phy_base[lchip] = virt_to_bus(high_memory); dma_phy_base[lchip] = virt_to_bus(high_memory);
dma_virt_base[lchip] = ioremap_nocache(dma_phy_base[lchip], size); /*
* ioremap has provided non-cached semantics by default
* since the Linux 2.6 days, so remove the additional
* ioremap_nocache interface.
*/
dma_virt_base[lchip] = ioremap(dma_phy_base[lchip], size);
} }
else else
{ {
@ -1205,7 +1210,12 @@ dal_alloc_dma_pool(int lchip, int size)
/* Get DMA memory from kernel */ /* Get DMA memory from kernel */
dma_virt_base[lchip] = _dal_pgalloc(size); dma_virt_base[lchip] = _dal_pgalloc(size);
dma_phy_base[lchip] = virt_to_bus(dma_virt_base[lchip]); dma_phy_base[lchip] = virt_to_bus(dma_virt_base[lchip]);
//dma_virt_base [lchip]= ioremap_nocache(dma_phy_base[lchip], size); /*
* ioremap has provided non-cached semantics by default
* since the Linux 2.6 days, so remove the additional
* ioremap_nocache interface.
*/
//dma_virt_base [lchip]= ioremap(dma_phy_base[lchip], size);
#endif #endif
} }
} }
@ -1943,7 +1953,12 @@ int linux_dal_pcie_probe(struct pci_dev* pdev, const struct pci_device_id* id)
} }
dev->phys_address = pci_resource_start(pdev, bar); dev->phys_address = pci_resource_start(pdev, bar);
dev->logic_address = (uintptr)ioremap_nocache(dev->phys_address, /*
* ioremap has provided non-cached semantics by default
* since the Linux 2.6 days, so remove the additional
* ioremap_nocache interface.
*/
dev->logic_address = (uintptr)ioremap(dev->phys_address,
pci_resource_len(dev->pci_dev, bar)); pci_resource_len(dev->pci_dev, bar));
/*0: little endian 1: big endian*/ /*0: little endian 1: big endian*/

View File

@ -1 +1,3 @@
obj-m := centec_e582_48x2q4z_platform.o centec_at24c64.o KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
obj-m := centec_e582_48x2q4z_platform.o

View File

@ -1,602 +0,0 @@
/*
* at24.c - handle most I2C EEPROMs
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/platform_data/at24.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
* Differences between different vendor product lines (like Atmel AT24C or
* MicroChip 24LC, etc) won't much matter for typical read/write access.
* There are also I2C RAM chips, likewise interchangeable. One example
* would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
*
* However, misconfiguration can lose data. "Set 16-bit memory address"
* to a part with 8-bit addressing will overwrite data. Writing with too
* big a page size also loses data. And it's not safe to assume that the
* conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
* uses 0x51, for just one example.
*
* Accordingly, explicit board-specific configuration data should be used
* in almost all cases. (One partial exception is an SMBus used to access
* "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
*
* So this driver uses "new style" I2C driver binding, expecting to be
* told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
* similar kernel-resident tables; or, configuration data coming from
* a bootloader.
*
* Other than binding model, current differences from "eeprom" driver are
* that this one handles write access and isn't restricted to 24c02 devices.
* It also handles larger devices (32 kbit and up) with two-byte addresses,
* which won't work on pure SMBus systems.
*/
struct at24_data {
struct at24_platform_data chip;
int use_smbus;
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
*/
struct mutex lock;
struct bin_attribute bin;
u8 *writebuf;
unsigned write_max;
unsigned num_addresses;
/*
* Some chips tie up multiple I2C addresses; dummy devices reserve
* them for us, and we'll use them with SMBus calls.
*/
struct i2c_client *client[];
};
/*
* This parameter is to help this driver avoid blocking other drivers out
* of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
* clock, one 256 byte read takes about 1/43 second which is excessive;
* but the 1/170 second it takes at 400 kHz may be quite reasonable; and
* at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
*
* This value is forced to be a power of two so that writes align on pages.
*/
static unsigned io_limit = 128;
module_param(io_limit, uint, 0);
MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)");
/*
* Specs often allow 5 msec for a page write, sometimes 20 msec;
* it's important to recover from write timeouts.
*/
static unsigned write_timeout = 25;
module_param(write_timeout, uint, 0);
MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
#define AT24_SIZE_BYTELEN 5
#define AT24_SIZE_FLAGS 8
#define AT24_BITMASK(x) (BIT(x) - 1)
/* create non-zero magic value for given eeprom parameters */
#define AT24_DEVICE_MAGIC(_len, _flags) \
((1 << AT24_SIZE_FLAGS | (_flags)) \
<< AT24_SIZE_BYTELEN | ilog2(_len))
static const struct i2c_device_id at24_ctc_ids[] = {
{ "24c64-ctc", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16 | AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, at24_ctc_ids);
/*-------------------------------------------------------------------------*/
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
* Assumes that sanity checks for offset happened at sysfs-layer.
*/
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
unsigned *offset)
{
unsigned i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16) {
i = *offset >> 16;
*offset &= 0xffff;
} else {
i = *offset >> 8;
*offset &= 0xff;
}
return at24->client[i];
}
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
unsigned offset, size_t count)
{
struct i2c_msg msg[2];
struct i2c_client *client;
unsigned long timeout, read_time;
int status;
memset(msg, 0, sizeof(msg));
/*
* REVISIT some multi-address chips don't rollover page reads to
* the next slave address, so we may need to truncate the count.
* Those chips might need another quirk flag.
*
* If the real hardware used four adjacent 24c02 chips and that
* were misconfigured as one 24c08, that would be a similar effect:
* one "eeprom" file not four, but larger reads would fail when
* they crossed certain pages.
*/
/*
* Slave address and byte offset derive from the offset. Always
* set the byte address; on a multi-master board, another master
* may have changed the chip's "current" address pointer.
*/
client = at24_translate_offset(at24, &offset);
if (count > io_limit)
count = io_limit;
count = 1;
/*
* Reads fail if the previous write didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
read_time = jiffies;
status = i2c_smbus_write_byte_data(client, (offset >> 8) & 0x0ff, offset & 0x0ff );
status = i2c_smbus_read_byte(client);
if (status >= 0) {
buf[0] = status;
status = count;
}
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(read_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
memset(buf, 0, count);
/*
* Read data from chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_read(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_read(at24, buf, off, count);
}
/*
* Note that if the hardware write-protect pin is pulled high, the whole
* chip is normally write protected. But there are plenty of product
* variants here, including OTP fuses and partial chip protect.
*
* We only use page mode writes; the alternative is sloooow. This routine
* writes at most one page.
*/
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
unsigned offset, size_t count)
{
struct i2c_client *client;
ssize_t status;
unsigned long timeout, write_time;
unsigned next_page;
/* Get corresponding I2C address and adjust offset */
client = at24_translate_offset(at24, &offset);
/* write_max is at most a page */
if (count > at24->write_max)
count = at24->write_max;
/* Never roll over backwards, to the start of this page */
next_page = roundup(offset + 1, at24->chip.page_size);
if (offset + count > next_page)
count = next_page - offset;
/*
* Writes fail if the previous one didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
write_time = jiffies;
status = i2c_smbus_write_word_data(client, (offset >> 8) & 0x0ff, (offset & 0xFF) | buf[0]);
if (status == 0)
status = count;
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(write_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
/*
* Write data to chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_write(at24, buf, off, 1); /* only one-byte to write; TODO page wirte */
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
if (unlikely(off >= attr->size))
return -EFBIG;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{
const __be32 *val;
struct device_node *node = client->dev.of_node;
if (node) {
if (of_get_property(node, "read-only", NULL))
chip->flags |= AT24_FLAG_READONLY;
val = of_get_property(node, "pagesize", NULL);
if (val)
chip->page_size = be32_to_cpup(val);
}
}
#else
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{ }
#endif /* CONFIG_OF */
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
bool writable;
int use_smbus = 0;
struct at24_data *at24;
int err;
unsigned i, num_addresses;
kernel_ulong_t magic;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
if (!id->driver_data)
return -ENODEV;
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
/*
* This is slow, but we can't know all eeproms, so we better
* play safe. Specifying custom eeprom-types via platform_data
* is recommended anyhow.
*/
chip.page_size = 1;
/* update chipdata if OF is present */
at24_get_ofdata(client, &chip);
chip.setup = NULL;
chip.context = NULL;
}
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
if (!chip.page_size) {
dev_err(&client->dev, "page_size must not be 0!\n");
return -EINVAL;
}
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {
use_smbus = I2C_SMBUS_WORD_DATA;
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus = I2C_SMBUS_BYTE_DATA;
} else {
return -EPFNOSUPPORT;
}
use_smbus = I2C_SMBUS_BYTE_DATA;
}
if (chip.flags & AT24_FLAG_TAKE8ADDR)
num_addresses = 8;
else
num_addresses = DIV_ROUND_UP(chip.byte_len, (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
if (!at24)
return -ENOMEM;
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
at24->chip = chip;
at24->num_addresses = num_addresses;
printk(KERN_ALERT "at24_probe chip.byte_len = 0x%x\n", chip.byte_len);
printk(KERN_ALERT "at24_probe chip.flags = 0x%x\n", chip.flags);
printk(KERN_ALERT "at24_probe chip.magic = 0x%lx\n", id->driver_data);
printk(KERN_ALERT "at24_probe use_smbus = %d\n", at24->use_smbus);
printk(KERN_ALERT "at24_probe num_addresses = %d\n", at24->num_addresses);
/*
* Export the EEPROM bytes through sysfs, since that's convenient.
* By default, only root should see the data (maybe passwords etc)
*/
sysfs_bin_attr_init(&at24->bin);
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
unsigned write_max = chip.page_size;
at24->bin.write = at24_bin_write;
at24->bin.attr.mode |= S_IWUSR;
if (write_max > io_limit)
write_max = io_limit;
if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
write_max = I2C_SMBUS_BLOCK_MAX;
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
at24->writebuf = devm_kzalloc(&client->dev, write_max + 2, GFP_KERNEL);
if (!at24->writebuf)
return -ENOMEM;
} else {
dev_warn(&client->dev, "cannot write due to controller restrictions.");
}
}
at24->client[0] = client;
/* use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++) {
at24->client[i] = i2c_new_dummy(client->adapter, client->addr + i);
if (!at24->client[i]) {
dev_err(&client->dev, "address 0x%02x unavailable\n", client->addr + i);
err = -EADDRINUSE;
goto err_clients;
}
}
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
if (err)
goto err_clients;
i2c_set_clientdata(client, at24);
printk(KERN_ALERT "at24_probe %s done\n", client->name);
return 0;
err_clients:
for (i = 1; i < num_addresses; i++)
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
return err;
}
static int at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
int i;
at24 = i2c_get_clientdata(client);
sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
return 0;
}
/*-------------------------------------------------------------------------*/
static struct i2c_board_info i2c_devs = {
I2C_BOARD_INFO("24c64-ctc", 0x57),
};
static struct i2c_adapter *adapter = NULL;
static struct i2c_client *client = NULL;
static int ctc_at24c64_init(void)
{
printk(KERN_ALERT "ctc_at24c64_init\n");
adapter = i2c_get_adapter(0);
if(adapter == NULL){
printk(KERN_ALERT "i2c_get_adapter == NULL\n");
return -1;
}
client = i2c_new_device(adapter, &i2c_devs);
if(client == NULL){
printk(KERN_ALERT "i2c_new_device == NULL\n");
i2c_put_adapter(adapter);
adapter = NULL;
return -1;
}
return 0;
}
static void ctc_at24c64_exit(void)
{
printk(KERN_ALERT "ctc_at24c64_exit\n");
if(client){
i2c_unregister_device(client);
}
if(adapter){
i2c_put_adapter(adapter);
}
}
static struct i2c_driver at24_ctc_driver = {
.driver = {
.name = "at24-ctc",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ctc_ids,
};
static int __init at24_ctc_init(void)
{
if (!io_limit) {
pr_err("at24_ctc: io_limit must not be 0!\n");
return -EINVAL;
}
io_limit = rounddown_pow_of_two(io_limit);
ctc_at24c64_init();
return i2c_add_driver(&at24_ctc_driver);
}
module_init(at24_ctc_init);
static void __exit at24_ctc_exit(void)
{
ctc_at24c64_exit();
i2c_del_driver(&at24_ctc_driver);
}
module_exit(at24_ctc_exit);
MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
MODULE_AUTHOR("David Brownell and Wolfram Sang");
MODULE_LICENSE("GPL");
/* XXX */

View File

@ -1,7 +1,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_data/pca954x.h> #include "../../pca954x/ctc-pca954x.h"
#include <linux/device.h> #include <linux/device.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/leds.h> #include <linux/leds.h>
@ -95,7 +95,7 @@ static struct pca954x_platform_data i2c_dev_pca9548_platform_data = {
.num_modes = PCA9548_CHANNEL_NUM, .num_modes = PCA9548_CHANNEL_NUM,
}; };
static struct i2c_board_info i2c_dev_pca9548 = { static struct i2c_board_info i2c_dev_pca9548 = {
I2C_BOARD_INFO("pca9548", 0x70), I2C_BOARD_INFO("ctc_pca9548", 0x70),
.platform_data = &i2c_dev_pca9548_platform_data, .platform_data = &i2c_dev_pca9548_platform_data,
}; };
static struct i2c_client *i2c_client_pca9548x = NULL; static struct i2c_client *i2c_client_pca9548x = NULL;
@ -110,7 +110,7 @@ static int e582_48x2q4z_init_i2c_pca9548(void)
} }
/* install i2c-mux */ /* install i2c-mux */
i2c_client_pca9548x = i2c_new_device(i2c_adp_master, &i2c_dev_pca9548); i2c_client_pca9548x = i2c_new_client_device(i2c_adp_master, &i2c_dev_pca9548);
if(IS_INVALID_PTR(i2c_client_pca9548x)) if(IS_INVALID_PTR(i2c_client_pca9548x))
{ {
i2c_client_pca9548x = NULL; i2c_client_pca9548x = NULL;
@ -150,7 +150,7 @@ static int e582_48x2q4z_init_i2c_adt7470(void)
return -1; return -1;
} }
i2c_client_adt7470 = i2c_new_device(i2c_adp_adt7470, &i2c_dev_adt7470); i2c_client_adt7470 = i2c_new_client_device(i2c_adp_adt7470, &i2c_dev_adt7470);
if(IS_INVALID_PTR(i2c_client_adt7470)){ if(IS_INVALID_PTR(i2c_client_adt7470)){
i2c_client_adt7470 = NULL; i2c_client_adt7470 = NULL;
printk(KERN_CRIT "install e582_48x2q4z board adt7470 failed\n"); printk(KERN_CRIT "install e582_48x2q4z board adt7470 failed\n");
@ -206,14 +206,14 @@ static int e582_48x2q4z_init_i2c_psu(void)
return -1; return -1;
} }
i2c_client_psu1 = i2c_new_device(i2c_adp_psu1, &i2c_dev_psu1); i2c_client_psu1 = i2c_new_client_device(i2c_adp_psu1, &i2c_dev_psu1);
if(IS_INVALID_PTR(i2c_client_psu1)){ if(IS_INVALID_PTR(i2c_client_psu1)){
i2c_client_psu1 = NULL; i2c_client_psu1 = NULL;
printk(KERN_CRIT "create e582_48x2q4z board i2c client psu1 failed\n"); printk(KERN_CRIT "create e582_48x2q4z board i2c client psu1 failed\n");
return -1; return -1;
} }
i2c_client_psu2 = i2c_new_device(i2c_adp_psu2, &i2c_dev_psu2); i2c_client_psu2 = i2c_new_client_device(i2c_adp_psu2, &i2c_dev_psu2);
if(IS_INVALID_PTR(i2c_client_psu2)){ if(IS_INVALID_PTR(i2c_client_psu2)){
i2c_client_psu2 = NULL; i2c_client_psu2 = NULL;
printk(KERN_CRIT "create e582_48x2q4z board i2c client psu2 failed\n"); printk(KERN_CRIT "create e582_48x2q4z board i2c client psu2 failed\n");
@ -265,7 +265,7 @@ static int e582_48x2q4z_init_i2c_epld(void)
return -1; return -1;
} }
i2c_client_epld = i2c_new_device(i2c_adp_master, &i2c_dev_epld); i2c_client_epld = i2c_new_client_device(i2c_adp_master, &i2c_dev_epld);
if(IS_INVALID_PTR(i2c_client_epld)) if(IS_INVALID_PTR(i2c_client_epld))
{ {
i2c_client_epld = NULL; i2c_client_epld = NULL;
@ -362,7 +362,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio0 = i2c_new_device(i2c_adp_gpio0, &i2c_dev_gpio0); i2c_client_gpio0 = i2c_new_client_device(i2c_adp_gpio0, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0)) if(IS_INVALID_PTR(i2c_client_gpio0))
{ {
i2c_client_gpio0 = NULL; i2c_client_gpio0 = NULL;
@ -370,7 +370,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio1 = i2c_new_device(i2c_adp_gpio1, &i2c_dev_gpio1); i2c_client_gpio1 = i2c_new_client_device(i2c_adp_gpio1, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1)) if(IS_INVALID_PTR(i2c_client_gpio1))
{ {
i2c_client_gpio1 = NULL; i2c_client_gpio1 = NULL;
@ -378,7 +378,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio2 = i2c_new_device(i2c_adp_gpio2, &i2c_dev_gpio2); i2c_client_gpio2 = i2c_new_client_device(i2c_adp_gpio2, &i2c_dev_gpio2);
if(IS_INVALID_PTR(i2c_client_gpio2)) if(IS_INVALID_PTR(i2c_client_gpio2))
{ {
i2c_client_gpio2 = NULL; i2c_client_gpio2 = NULL;
@ -386,7 +386,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio3 = i2c_new_device(i2c_adp_gpio3, &i2c_dev_gpio3); i2c_client_gpio3 = i2c_new_client_device(i2c_adp_gpio3, &i2c_dev_gpio3);
if(IS_INVALID_PTR(i2c_client_gpio3)) if(IS_INVALID_PTR(i2c_client_gpio3))
{ {
i2c_client_gpio3 = NULL; i2c_client_gpio3 = NULL;
@ -394,7 +394,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio4 = i2c_new_device(i2c_adp_gpio4, &i2c_dev_gpio4); i2c_client_gpio4 = i2c_new_client_device(i2c_adp_gpio4, &i2c_dev_gpio4);
if(IS_INVALID_PTR(i2c_client_gpio4)) if(IS_INVALID_PTR(i2c_client_gpio4))
{ {
i2c_client_gpio4 = NULL; i2c_client_gpio4 = NULL;

View File

@ -25,12 +25,13 @@ if [ "$1" == "init" ]; then
modprobe i2c-dev modprobe i2c-dev
modprobe i2c-mux modprobe i2c-mux
modprobe i2c-smbus modprobe i2c-smbus
modprobe i2c-mux-pca954x force_deselect_on_exit=1 modprobe ctc-i2c-mux-pca954x force_deselect_on_exit=1
i2cset -y 0 0x58 0x8 0x3f i2cset -y 0 0x58 0x8 0x3f
modprobe lm77 modprobe lm77
modprobe tun modprobe tun
modprobe dal modprobe dal
modprobe centec_at24c64 modprobe at24
echo 24c64 0x57 > /sys/bus/i2c/devices/i2c-0/new_device
modprobe centec_e582_48x2q4z_platform modprobe centec_e582_48x2q4z_platform
i2cset -y 15 0x21 0x18 0x0 i2cset -y 15 0x21 0x18 0x0
i2cset -y 15 0x21 0x19 0x0 i2cset -y 15 0x21 0x19 0x0
@ -69,9 +70,9 @@ elif [ "$1" == "deinit" ]; then
kill -9 $(pidof platform_monitor) > /dev/null 2>&1 kill -9 $(pidof platform_monitor) > /dev/null 2>&1
rm -rf /usr/bin/platform_monitor rm -rf /usr/bin/platform_monitor
modprobe -r centec_e582_48x2q4z_platform modprobe -r centec_e582_48x2q4z_platform
modprobe -r centec_at24c64 modprobe -r at24
modprobe -r dal modprobe -r dal
modprobe -r i2c-mux-pca954x modprobe -r ctc-i2c-mux-pca954x
modprobe -r i2c-dev modprobe -r i2c-dev
else else
echo "e582-48x2q4z_platform : Invalid option !" echo "e582-48x2q4z_platform : Invalid option !"

View File

@ -1 +1,3 @@
obj-m := centec_e582_48x6q_platform.o centec_at24c64.o KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec/sonic-platform-modules-e582/pca954x/Module.symvers
obj-m := centec_e582_48x6q_platform.o

View File

@ -1,602 +0,0 @@
/*
* at24.c - handle most I2C EEPROMs
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/platform_data/at24.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
* Differences between different vendor product lines (like Atmel AT24C or
* MicroChip 24LC, etc) won't much matter for typical read/write access.
* There are also I2C RAM chips, likewise interchangeable. One example
* would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
*
* However, misconfiguration can lose data. "Set 16-bit memory address"
* to a part with 8-bit addressing will overwrite data. Writing with too
* big a page size also loses data. And it's not safe to assume that the
* conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
* uses 0x51, for just one example.
*
* Accordingly, explicit board-specific configuration data should be used
* in almost all cases. (One partial exception is an SMBus used to access
* "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.)
*
* So this driver uses "new style" I2C driver binding, expecting to be
* told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
* similar kernel-resident tables; or, configuration data coming from
* a bootloader.
*
* Other than binding model, current differences from "eeprom" driver are
* that this one handles write access and isn't restricted to 24c02 devices.
* It also handles larger devices (32 kbit and up) with two-byte addresses,
* which won't work on pure SMBus systems.
*/
struct at24_data {
struct at24_platform_data chip;
int use_smbus;
/*
* Lock protects against activities from other Linux tasks,
* but not from changes by other I2C masters.
*/
struct mutex lock;
struct bin_attribute bin;
u8 *writebuf;
unsigned write_max;
unsigned num_addresses;
/*
* Some chips tie up multiple I2C addresses; dummy devices reserve
* them for us, and we'll use them with SMBus calls.
*/
struct i2c_client *client[];
};
/*
* This parameter is to help this driver avoid blocking other drivers out
* of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
* clock, one 256 byte read takes about 1/43 second which is excessive;
* but the 1/170 second it takes at 400 kHz may be quite reasonable; and
* at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
*
* This value is forced to be a power of two so that writes align on pages.
*/
static unsigned io_limit = 128;
module_param(io_limit, uint, 0);
MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)");
/*
* Specs often allow 5 msec for a page write, sometimes 20 msec;
* it's important to recover from write timeouts.
*/
static unsigned write_timeout = 25;
module_param(write_timeout, uint, 0);
MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
#define AT24_SIZE_BYTELEN 5
#define AT24_SIZE_FLAGS 8
#define AT24_BITMASK(x) (BIT(x) - 1)
/* create non-zero magic value for given eeprom parameters */
#define AT24_DEVICE_MAGIC(_len, _flags) \
((1 << AT24_SIZE_FLAGS | (_flags)) \
<< AT24_SIZE_BYTELEN | ilog2(_len))
static const struct i2c_device_id at24_ctc_ids[] = {
{ "24c64-ctc", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16 | AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(i2c, at24_ctc_ids);
/*-------------------------------------------------------------------------*/
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
* Assumes that sanity checks for offset happened at sysfs-layer.
*/
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
unsigned *offset)
{
unsigned i = 0;
if (at24->chip.flags & AT24_FLAG_ADDR16) {
i = *offset >> 16;
*offset &= 0xffff;
} else {
i = *offset >> 8;
*offset &= 0xff;
}
return at24->client[i];
}
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
unsigned offset, size_t count)
{
struct i2c_msg msg[2];
struct i2c_client *client;
unsigned long timeout, read_time;
int status;
memset(msg, 0, sizeof(msg));
/*
* REVISIT some multi-address chips don't rollover page reads to
* the next slave address, so we may need to truncate the count.
* Those chips might need another quirk flag.
*
* If the real hardware used four adjacent 24c02 chips and that
* were misconfigured as one 24c08, that would be a similar effect:
* one "eeprom" file not four, but larger reads would fail when
* they crossed certain pages.
*/
/*
* Slave address and byte offset derive from the offset. Always
* set the byte address; on a multi-master board, another master
* may have changed the chip's "current" address pointer.
*/
client = at24_translate_offset(at24, &offset);
if (count > io_limit)
count = io_limit;
count = 1;
/*
* Reads fail if the previous write didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
read_time = jiffies;
status = i2c_smbus_write_byte_data(client, (offset >> 8) & 0x0ff, offset & 0x0ff );
status = i2c_smbus_read_byte(client);
if (status >= 0) {
buf[0] = status;
status = count;
}
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(read_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
memset(buf, 0, count);
/*
* Read data from chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_read(at24, buf, off, count);
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_read(at24, buf, off, count);
}
/*
* Note that if the hardware write-protect pin is pulled high, the whole
* chip is normally write protected. But there are plenty of product
* variants here, including OTP fuses and partial chip protect.
*
* We only use page mode writes; the alternative is sloooow. This routine
* writes at most one page.
*/
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
unsigned offset, size_t count)
{
struct i2c_client *client;
ssize_t status;
unsigned long timeout, write_time;
unsigned next_page;
/* Get corresponding I2C address and adjust offset */
client = at24_translate_offset(at24, &offset);
/* write_max is at most a page */
if (count > at24->write_max)
count = at24->write_max;
/* Never roll over backwards, to the start of this page */
next_page = roundup(offset + 1, at24->chip.page_size);
if (offset + count > next_page)
count = next_page - offset;
/*
* Writes fail if the previous one didn't complete yet. We may
* loop a few times until this one succeeds, waiting at least
* long enough for one entire page write to work.
*/
timeout = jiffies + msecs_to_jiffies(write_timeout);
do {
write_time = jiffies;
status = i2c_smbus_write_word_data(client, (offset >> 8) & 0x0ff, (offset & 0xFF) | buf[0]);
if (status == 0)
status = count;
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", count, offset, status, jiffies);
if (status == count)
return count;
/* REVISIT: at HZ=100, this is sloooow */
msleep(1);
} while (time_before(write_time, timeout));
return -ETIMEDOUT;
}
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
ssize_t retval = 0;
if (unlikely(!count))
return count;
/*
* Write data to chip, protecting against concurrent updates
* from this host, but not from other I2C masters.
*/
mutex_lock(&at24->lock);
while (count) {
ssize_t status;
status = at24_eeprom_write(at24, buf, off, 1); /* only one-byte to write; TODO page wirte */
if (status <= 0) {
if (retval == 0)
retval = status;
break;
}
buf += status;
off += status;
count -= status;
retval += status;
}
mutex_unlock(&at24->lock);
return retval;
}
static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
if (unlikely(off >= attr->size))
return -EFBIG;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_OF
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{
const __be32 *val;
struct device_node *node = client->dev.of_node;
if (node) {
if (of_get_property(node, "read-only", NULL))
chip->flags |= AT24_FLAG_READONLY;
val = of_get_property(node, "pagesize", NULL);
if (val)
chip->page_size = be32_to_cpup(val);
}
}
#else
static void at24_get_ofdata(struct i2c_client *client,
struct at24_platform_data *chip)
{ }
#endif /* CONFIG_OF */
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct at24_platform_data chip;
bool writable;
int use_smbus = 0;
struct at24_data *at24;
int err;
unsigned i, num_addresses;
kernel_ulong_t magic;
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
if (!id->driver_data)
return -ENODEV;
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
/*
* This is slow, but we can't know all eeproms, so we better
* play safe. Specifying custom eeprom-types via platform_data
* is recommended anyhow.
*/
chip.page_size = 1;
/* update chipdata if OF is present */
at24_get_ofdata(client, &chip);
chip.setup = NULL;
chip.context = NULL;
}
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
if (!chip.page_size) {
dev_err(&client->dev, "page_size must not be 0!\n");
return -EINVAL;
}
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
/* Use I2C operations unless we're stuck with SMBus extensions. */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) {
use_smbus = I2C_SMBUS_WORD_DATA;
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus = I2C_SMBUS_BYTE_DATA;
} else {
return -EPFNOSUPPORT;
}
use_smbus = I2C_SMBUS_BYTE_DATA;
}
if (chip.flags & AT24_FLAG_TAKE8ADDR)
num_addresses = 8;
else
num_addresses = DIV_ROUND_UP(chip.byte_len, (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
if (!at24)
return -ENOMEM;
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
at24->chip = chip;
at24->num_addresses = num_addresses;
printk(KERN_ALERT "at24_probe chip.byte_len = 0x%x\n", chip.byte_len);
printk(KERN_ALERT "at24_probe chip.flags = 0x%x\n", chip.flags);
printk(KERN_ALERT "at24_probe chip.magic = 0x%lx\n", id->driver_data);
printk(KERN_ALERT "at24_probe use_smbus = %d\n", at24->use_smbus);
printk(KERN_ALERT "at24_probe num_addresses = %d\n", at24->num_addresses);
/*
* Export the EEPROM bytes through sysfs, since that's convenient.
* By default, only root should see the data (maybe passwords etc)
*/
sysfs_bin_attr_init(&at24->bin);
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;
writable = !(chip.flags & AT24_FLAG_READONLY);
if (writable) {
if (!use_smbus || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
unsigned write_max = chip.page_size;
at24->bin.write = at24_bin_write;
at24->bin.attr.mode |= S_IWUSR;
if (write_max > io_limit)
write_max = io_limit;
if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
write_max = I2C_SMBUS_BLOCK_MAX;
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
at24->writebuf = devm_kzalloc(&client->dev, write_max + 2, GFP_KERNEL);
if (!at24->writebuf)
return -ENOMEM;
} else {
dev_warn(&client->dev, "cannot write due to controller restrictions.");
}
}
at24->client[0] = client;
/* use dummy devices for multiple-address chips */
for (i = 1; i < num_addresses; i++) {
at24->client[i] = i2c_new_dummy(client->adapter, client->addr + i);
if (!at24->client[i]) {
dev_err(&client->dev, "address 0x%02x unavailable\n", client->addr + i);
err = -EADDRINUSE;
goto err_clients;
}
}
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
if (err)
goto err_clients;
i2c_set_clientdata(client, at24);
printk(KERN_ALERT "at24_probe %s done\n", client->name);
return 0;
err_clients:
for (i = 1; i < num_addresses; i++)
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
return err;
}
static int at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
int i;
at24 = i2c_get_clientdata(client);
sysfs_remove_bin_file(&client->dev.kobj, &at24->bin);
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
return 0;
}
/*-------------------------------------------------------------------------*/
static struct i2c_board_info i2c_devs = {
I2C_BOARD_INFO("24c64-ctc", 0x57),
};
static struct i2c_adapter *adapter = NULL;
static struct i2c_client *client = NULL;
static int ctc_at24c64_init(void)
{
printk(KERN_ALERT "ctc_at24c64_init\n");
adapter = i2c_get_adapter(0);
if(adapter == NULL){
printk(KERN_ALERT "i2c_get_adapter == NULL\n");
return -1;
}
client = i2c_new_device(adapter, &i2c_devs);
if(client == NULL){
printk(KERN_ALERT "i2c_new_device == NULL\n");
i2c_put_adapter(adapter);
adapter = NULL;
return -1;
}
return 0;
}
static void ctc_at24c64_exit(void)
{
printk(KERN_ALERT "ctc_at24c64_exit\n");
if(client){
i2c_unregister_device(client);
}
if(adapter){
i2c_put_adapter(adapter);
}
}
static struct i2c_driver at24_ctc_driver = {
.driver = {
.name = "at24-ctc",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = at24_remove,
.id_table = at24_ctc_ids,
};
static int __init at24_ctc_init(void)
{
if (!io_limit) {
pr_err("at24_ctc: io_limit must not be 0!\n");
return -EINVAL;
}
io_limit = rounddown_pow_of_two(io_limit);
ctc_at24c64_init();
return i2c_add_driver(&at24_ctc_driver);
}
module_init(at24_ctc_init);
static void __exit at24_ctc_exit(void)
{
ctc_at24c64_exit();
i2c_del_driver(&at24_ctc_driver);
}
module_exit(at24_ctc_exit);
MODULE_DESCRIPTION("Driver for most I2C EEPROMs");
MODULE_AUTHOR("David Brownell and Wolfram Sang");
MODULE_LICENSE("GPL");
/* XXX */

View File

@ -1,7 +1,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/platform_data/pca954x.h> #include "../../pca954x/ctc-pca954x.h"
#include <linux/device.h> #include <linux/device.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
#include <linux/leds.h> #include <linux/leds.h>
@ -95,7 +95,7 @@ static struct pca954x_platform_data i2c_dev_pca9548_platform_data = {
.num_modes = PCA9548_CHANNEL_NUM, .num_modes = PCA9548_CHANNEL_NUM,
}; };
static struct i2c_board_info i2c_dev_pca9548 = { static struct i2c_board_info i2c_dev_pca9548 = {
I2C_BOARD_INFO("pca9548", 0x70), I2C_BOARD_INFO("ctc_pca9548", 0x70),
.platform_data = &i2c_dev_pca9548_platform_data, .platform_data = &i2c_dev_pca9548_platform_data,
}; };
static struct i2c_client *i2c_client_pca9548x = NULL; static struct i2c_client *i2c_client_pca9548x = NULL;
@ -110,7 +110,7 @@ static int e582_48x6q_init_i2c_pca9548(void)
} }
/* install i2c-mux */ /* install i2c-mux */
i2c_client_pca9548x = i2c_new_device(i2c_adp_master, &i2c_dev_pca9548); i2c_client_pca9548x = i2c_new_client_device(i2c_adp_master, &i2c_dev_pca9548);
if(IS_INVALID_PTR(i2c_client_pca9548x)) if(IS_INVALID_PTR(i2c_client_pca9548x))
{ {
i2c_client_pca9548x = NULL; i2c_client_pca9548x = NULL;
@ -150,7 +150,7 @@ static int e582_48x6q_init_i2c_adt7470(void)
return -1; return -1;
} }
i2c_client_adt7470 = i2c_new_device(i2c_adp_adt7470, &i2c_dev_adt7470); i2c_client_adt7470 = i2c_new_client_device(i2c_adp_adt7470, &i2c_dev_adt7470);
if(IS_INVALID_PTR(i2c_client_adt7470)){ if(IS_INVALID_PTR(i2c_client_adt7470)){
i2c_client_adt7470 = NULL; i2c_client_adt7470 = NULL;
printk(KERN_CRIT "install e582_48x6q board adt7470 failed\n"); printk(KERN_CRIT "install e582_48x6q board adt7470 failed\n");
@ -206,14 +206,14 @@ static int e582_48x6q_init_i2c_psu(void)
return -1; return -1;
} }
i2c_client_psu1 = i2c_new_device(i2c_adp_psu1, &i2c_dev_psu1); i2c_client_psu1 = i2c_new_client_device(i2c_adp_psu1, &i2c_dev_psu1);
if(IS_INVALID_PTR(i2c_client_psu1)){ if(IS_INVALID_PTR(i2c_client_psu1)){
i2c_client_psu1 = NULL; i2c_client_psu1 = NULL;
printk(KERN_CRIT "create e582_48x6q board i2c client psu1 failed\n"); printk(KERN_CRIT "create e582_48x6q board i2c client psu1 failed\n");
return -1; return -1;
} }
i2c_client_psu2 = i2c_new_device(i2c_adp_psu2, &i2c_dev_psu2); i2c_client_psu2 = i2c_new_client_device(i2c_adp_psu2, &i2c_dev_psu2);
if(IS_INVALID_PTR(i2c_client_psu2)){ if(IS_INVALID_PTR(i2c_client_psu2)){
i2c_client_psu2 = NULL; i2c_client_psu2 = NULL;
printk(KERN_CRIT "create e582_48x6q board i2c client psu2 failed\n"); printk(KERN_CRIT "create e582_48x6q board i2c client psu2 failed\n");
@ -265,7 +265,7 @@ static int e582_48x6q_init_i2c_epld(void)
return -1; return -1;
} }
i2c_client_epld = i2c_new_device(i2c_adp_master, &i2c_dev_epld); i2c_client_epld = i2c_new_client_device(i2c_adp_master, &i2c_dev_epld);
if(IS_INVALID_PTR(i2c_client_epld)) if(IS_INVALID_PTR(i2c_client_epld))
{ {
i2c_client_epld = NULL; i2c_client_epld = NULL;
@ -317,7 +317,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio0 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio0); i2c_client_gpio0 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio0);
if(IS_INVALID_PTR(i2c_client_gpio0)) if(IS_INVALID_PTR(i2c_client_gpio0))
{ {
i2c_client_gpio0 = NULL; i2c_client_gpio0 = NULL;
@ -325,7 +325,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio1 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio1); i2c_client_gpio1 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio1);
if(IS_INVALID_PTR(i2c_client_gpio1)) if(IS_INVALID_PTR(i2c_client_gpio1))
{ {
i2c_client_gpio1 = NULL; i2c_client_gpio1 = NULL;
@ -333,7 +333,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio2 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio2); i2c_client_gpio2 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio2);
if(IS_INVALID_PTR(i2c_client_gpio2)) if(IS_INVALID_PTR(i2c_client_gpio2))
{ {
i2c_client_gpio2 = NULL; i2c_client_gpio2 = NULL;
@ -341,7 +341,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio3 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio3); i2c_client_gpio3 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio3);
if(IS_INVALID_PTR(i2c_client_gpio3)) if(IS_INVALID_PTR(i2c_client_gpio3))
{ {
i2c_client_gpio3 = NULL; i2c_client_gpio3 = NULL;
@ -349,7 +349,7 @@ static int e582_48x6q_init_i2c_gpio(void)
return -1; return -1;
} }
i2c_client_gpio4 = i2c_new_device(i2c_adp_master, &i2c_dev_gpio4); i2c_client_gpio4 = i2c_new_client_device(i2c_adp_master, &i2c_dev_gpio4);
if(IS_INVALID_PTR(i2c_client_gpio4)) if(IS_INVALID_PTR(i2c_client_gpio4))
{ {
i2c_client_gpio4 = NULL; i2c_client_gpio4 = NULL;

View File

@ -25,7 +25,7 @@ if [ "$1" == "init" ]; then
modprobe i2c-dev modprobe i2c-dev
modprobe i2c-mux modprobe i2c-mux
modprobe i2c-smbus modprobe i2c-smbus
modprobe i2c-mux-pca954x force_deselect_on_exit=1 modprobe ctc-i2c-mux-pca954x force_deselect_on_exit=1
i2cset -y 0 0x58 0x8 0x3f i2cset -y 0 0x58 0x8 0x3f
i2cset -y 0 0x20 0x1b 0x0 i2cset -y 0 0x20 0x1b 0x0
i2cset -y 0 0x20 0xb 0x0 i2cset -y 0 0x20 0xb 0x0
@ -42,7 +42,8 @@ if [ "$1" == "init" ]; then
modprobe lm77 modprobe lm77
modprobe tun modprobe tun
modprobe dal modprobe dal
modprobe centec_at24c64 modprobe at24
echo 24c64 0x57 > /sys/bus/i2c/devices/i2c-0/new_device
modprobe centec_e582_48x6q_platform modprobe centec_e582_48x6q_platform
#start platform monitor #start platform monitor
@ -53,9 +54,9 @@ elif [ "$1" == "deinit" ]; then
kill -9 $(pidof platform_monitor) > /dev/null 2>&1 kill -9 $(pidof platform_monitor) > /dev/null 2>&1
rm -rf /usr/bin/platform_monitor rm -rf /usr/bin/platform_monitor
modprobe -r centec_e582_48x6q_platform modprobe -r centec_e582_48x6q_platform
modprobe -r centec_at24c64 modprobe -r at24
modprobe -r dal modprobe -r dal
modprobe -r i2c-mux-pca954x modprobe -r ctc-i2c-mux-pca954x
modprobe -r i2c-dev modprobe -r i2c-dev
else else
echo "e582-48x6q_platform : Invalid option !" echo "e582-48x6q_platform : Invalid option !"

View File

@ -7,11 +7,11 @@ Standards-Version: 3.9.3
Package: platform-modules-e582-48x2q4z Package: platform-modules-e582-48x2q4z
Architecture: amd64 Architecture: amd64
Depends: linux-image-4.19.0-12-2-amd64-unsigned Depends: linux-image-5.10.0-8-2-amd64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp Description: kernel modules for platform devices such as fan, led, sfp
Package: platform-modules-e582-48x6q Package: platform-modules-e582-48x6q
Architecture: amd64 Architecture: amd64
Depends: linux-image-4.19.0-12-2-amd64-unsigned Depends: linux-image-5.10.0-8-2-amd64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp Description: kernel modules for platform devices such as fan, led, sfp

View File

@ -6,6 +6,7 @@ KVERSION ?= $(shell uname -r)
KERNEL_SRC := /lib/modules/$(KVERSION) KERNEL_SRC := /lib/modules/$(KVERSION)
MOD_SRC_DIR:= $(shell pwd) MOD_SRC_DIR:= $(shell pwd)
MODULE_DIRS:= 48x6q 48x2q4z MODULE_DIRS:= 48x6q 48x2q4z
PCA954X_DIR := pca954x
%: %:
dh $@ dh $@
@ -14,6 +15,9 @@ override_dh_auto_build:
(for mod in $(MODULE_DIRS); do \ (for mod in $(MODULE_DIRS); do \
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \ make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
done) done)
(for mod in $(PCA954X_DIR); do \
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \
done)
rm $(MOD_SRC_DIR)/centec-dal -rf rm $(MOD_SRC_DIR)/centec-dal -rf
cp $(MOD_SRC_DIR)/../centec-dal $(MOD_SRC_DIR)/centec-dal -rf cp $(MOD_SRC_DIR)/../centec-dal $(MOD_SRC_DIR)/centec-dal -rf
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/centec-dal make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/centec-dal
@ -25,6 +29,7 @@ override_dh_auto_install:
cp -f $(MOD_SRC_DIR)/$${mod}/modules/*.ko \ cp -f $(MOD_SRC_DIR)/$${mod}/modules/*.ko \
debian/platform-modules-e582-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ debian/platform-modules-e582-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp -f $(MOD_SRC_DIR)/centec-dal/*.ko debian/platform-modules-e582-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \ cp -f $(MOD_SRC_DIR)/centec-dal/*.ko debian/platform-modules-e582-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
cp -f $(MOD_SRC_DIR)/$(PCA954X_DIR)/*.ko debian/platform-modules-e582-$${mod}/$(KERNEL_SRC)/$(INSTALL_MOD_DIR); \
done) done)
override_dh_usrlocal: override_dh_usrlocal:

View File

@ -0,0 +1 @@
obj-m := ctc-i2c-mux-pca954x.o

View File

@ -0,0 +1,581 @@
// SPDX-License-Identifier: GPL-2.0
/*
* I2C multiplexer
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@linux.it>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@eurotech.it>
*
* This module supports the PCA954x and PCA984x series of I2C multiplexer/switch
* chips made by NXP Semiconductors.
* This includes the:
* PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547,
* PCA9548, PCA9846, PCA9847, PCA9848 and PCA9849.
*
* These chips are all controlled via the I2C bus itself, and all have a
* single 8-bit register. The upstream "parent" bus fans out to two,
* four, or eight downstream busses or channels; which of these
* are selected is determined by the chip type and register contents. A
* mux can select only one sub-bus at a time; a switch can select any
* combination simultaneously.
*
* Based on:
* pca954x.c from Kumar Gala <galak@kernel.crashing.org>
* Copyright (C) 2006
*
* Based on:
* pca954x.c from Ken Harrenstien
* Copyright (C) 2004 Google, Inc. (Ken Harrenstien)
*
* Based on:
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
* and
* pca9540.c from Jean Delvare <jdelvare@suse.de>.
*/
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <dt-bindings/mux/mux.h>
#include "ctc-pca954x.h"
#define PCA954X_MAX_NCHANS 8
#define PCA954X_IRQ_OFFSET 4
enum pca_type {
pca_9540,
pca_9542,
pca_9543,
pca_9544,
pca_9545,
pca_9546,
pca_9547,
pca_9548,
pca_9846,
pca_9847,
pca_9848,
pca_9849,
};
struct chip_desc {
u8 nchans;
u8 enable; /* used for muxes only */
u8 has_irq;
enum muxtype {
pca954x_ismux = 0,
pca954x_isswi
} muxtype;
struct i2c_device_identity id;
};
struct pca954x {
const struct chip_desc *chip;
u8 last_chan; /* last register value */
/* MUX_IDLE_AS_IS, MUX_IDLE_DISCONNECT or >= 0 for channel */
s32 idle_state;
struct i2c_client *client;
struct irq_domain *irq;
unsigned int irq_mask;
raw_spinlock_t lock;
};
/* Provide specs for the PCA954x types we know about */
static const struct chip_desc chips[] = {
[pca_9540] = {
.nchans = 2,
.enable = 0x4,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9542] = {
.nchans = 2,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9543] = {
.nchans = 2,
.has_irq = 1,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9544] = {
.nchans = 4,
.enable = 0x4,
.has_irq = 1,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9545] = {
.nchans = 4,
.has_irq = 1,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9546] = {
.nchans = 4,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9547] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9548] = {
.nchans = 8,
.muxtype = pca954x_isswi,
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
},
[pca_9846] = {
.nchans = 4,
.muxtype = pca954x_isswi,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x10b,
},
},
[pca_9847] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x108,
},
},
[pca_9848] = {
.nchans = 8,
.muxtype = pca954x_isswi,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x10a,
},
},
[pca_9849] = {
.nchans = 4,
.enable = 0x4,
.muxtype = pca954x_ismux,
.id = {
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
.part_id = 0x109,
},
},
};
static const struct i2c_device_id pca954x_id[] = {
{ "ctc_pca9540", pca_9540 },
{ "ctc_pca9542", pca_9542 },
{ "ctc_pca9543", pca_9543 },
{ "ctc_pca9544", pca_9544 },
{ "ctc_pca9545", pca_9545 },
{ "ctc_pca9546", pca_9546 },
{ "ctc_pca9547", pca_9547 },
{ "ctc_pca9548", pca_9548 },
{ "ctc_pca9846", pca_9846 },
{ "ctc_pca9847", pca_9847 },
{ "ctc_pca9848", pca_9848 },
{ "ctc_pca9849", pca_9849 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca954x_id);
static const struct of_device_id pca954x_of_match[] = {
{ .compatible = "nxp,ctc_pca9540", .data = &chips[pca_9540] },
{ .compatible = "nxp,ctc_pca9542", .data = &chips[pca_9542] },
{ .compatible = "nxp,ctc_pca9543", .data = &chips[pca_9543] },
{ .compatible = "nxp,ctc_pca9544", .data = &chips[pca_9544] },
{ .compatible = "nxp,ctc_pca9545", .data = &chips[pca_9545] },
{ .compatible = "nxp,ctc_pca9546", .data = &chips[pca_9546] },
{ .compatible = "nxp,ctc_pca9547", .data = &chips[pca_9547] },
{ .compatible = "nxp,ctc_pca9548", .data = &chips[pca_9548] },
{ .compatible = "nxp,ctc_pca9846", .data = &chips[pca_9846] },
{ .compatible = "nxp,ctc_pca9847", .data = &chips[pca_9847] },
{ .compatible = "nxp,ctc_pca9848", .data = &chips[pca_9848] },
{ .compatible = "nxp,ctc_pca9849", .data = &chips[pca_9849] },
{}
};
MODULE_DEVICE_TABLE(of, pca954x_of_match);
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
for this as they will try to lock adapter a second time */
static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
union i2c_smbus_data dummy;
return __i2c_smbus_xfer(adap, client->addr, client->flags,
I2C_SMBUS_WRITE, val,
I2C_SMBUS_BYTE, &dummy);
}
static u8 pca954x_regval(struct pca954x *data, u8 chan)
{
/* We make switches look like muxes, not sure how to be smarter. */
if (data->chip->muxtype == pca954x_ismux)
return chan | data->chip->enable;
else
return 1 << chan;
}
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
u8 regval;
int ret = 0;
regval = pca954x_regval(data, chan);
/* Only select the channel if its different from the last channel */
if (data->last_chan != regval) {
ret = pca954x_reg_write(muxc->parent, client, regval);
data->last_chan = ret < 0 ? 0 : regval;
}
return ret;
}
static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
s32 idle_state;
idle_state = READ_ONCE(data->idle_state);
if (idle_state >= 0)
/* Set the mux back to a predetermined channel */
return pca954x_select_chan(muxc, idle_state);
if (idle_state == MUX_IDLE_DISCONNECT) {
/* Deselect active channel */
data->last_chan = 0;
return pca954x_reg_write(muxc->parent, client,
data->last_chan);
}
/* otherwise leave as-is */
return 0;
}
static ssize_t idle_state_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
return sprintf(buf, "%d\n", READ_ONCE(data->idle_state));
}
static ssize_t idle_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int val;
int ret;
ret = kstrtoint(buf, 0, &val);
if (ret < 0)
return ret;
if (val != MUX_IDLE_AS_IS && val != MUX_IDLE_DISCONNECT &&
(val < 0 || val >= data->chip->nchans))
return -EINVAL;
i2c_lock_bus(muxc->parent, I2C_LOCK_SEGMENT);
WRITE_ONCE(data->idle_state, val);
/*
* Set the mux into a state consistent with the new
* idle_state.
*/
if (data->last_chan || val != MUX_IDLE_DISCONNECT)
ret = pca954x_deselect_mux(muxc, 0);
i2c_unlock_bus(muxc->parent, I2C_LOCK_SEGMENT);
return ret < 0 ? ret : count;
}
static DEVICE_ATTR_RW(idle_state);
static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
{
struct pca954x *data = dev_id;
unsigned long pending;
int ret, i;
ret = i2c_smbus_read_byte(data->client);
if (ret < 0)
return IRQ_NONE;
pending = (ret >> PCA954X_IRQ_OFFSET) & (BIT(data->chip->nchans) - 1);
for_each_set_bit(i, &pending, data->chip->nchans)
handle_nested_irq(irq_linear_revmap(data->irq, i));
return IRQ_RETVAL(pending);
}
static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
{
if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW)
return -EINVAL;
return 0;
}
static struct irq_chip pca954x_irq_chip = {
.name = "i2c-mux-pca954x",
.irq_set_type = pca954x_irq_set_type,
};
static int pca954x_irq_setup(struct i2c_mux_core *muxc)
{
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
int c, irq;
if (!data->chip->has_irq || client->irq <= 0)
return 0;
raw_spin_lock_init(&data->lock);
data->irq = irq_domain_add_linear(client->dev.of_node,
data->chip->nchans,
&irq_domain_simple_ops, data);
if (!data->irq)
return -ENODEV;
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_create_mapping(data->irq, c);
if (!irq) {
dev_err(&client->dev, "failed irq create map\n");
return -EINVAL;
}
irq_set_chip_data(irq, data);
irq_set_chip_and_handler(irq, &pca954x_irq_chip,
handle_simple_irq);
}
return 0;
}
static void pca954x_cleanup(struct i2c_mux_core *muxc)
{
struct pca954x *data = i2c_mux_priv(muxc);
int c, irq;
if (data->irq) {
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_find_mapping(data->irq, c);
irq_dispose_mapping(irq);
}
irq_domain_remove(data->irq);
}
i2c_mux_del_adapters(muxc);
}
static int pca954x_init(struct i2c_client *client, struct pca954x *data)
{
int ret;
if (data->idle_state >= 0)
data->last_chan = pca954x_regval(data, data->idle_state);
else
data->last_chan = 0; /* Disconnect multiplexer */
ret = i2c_smbus_write_byte(client, data->last_chan);
if (ret < 0)
data->last_chan = 0;
return ret;
}
/*
* I2C init/probing/exit functions
*/
static int pca954x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adap = client->adapter;
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
struct device *dev = &client->dev;
struct gpio_desc *gpio;
int num, force, class;
struct i2c_mux_core *muxc;
struct pca954x *data;
int ret;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
return -ENODEV;
muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0,
pca954x_select_chan, pca954x_deselect_mux);
if (!muxc)
return -ENOMEM;
data = i2c_mux_priv(muxc);
i2c_set_clientdata(client, muxc);
data->client = client;
/* Reset the mux if a reset GPIO is specified. */
gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
if (gpio) {
udelay(1);
gpiod_set_value_cansleep(gpio, 0);
/* Give the chip some time to recover. */
udelay(1);
}
data->chip = device_get_match_data(dev);
if (!data->chip)
data->chip = &chips[id->driver_data];
if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) {
struct i2c_device_identity id;
ret = i2c_get_device_id(client, &id);
if (ret && ret != -EOPNOTSUPP)
return ret;
if (!ret &&
(id.manufacturer_id != data->chip->id.manufacturer_id ||
id.part_id != data->chip->id.part_id)) {
dev_warn(dev, "unexpected device id %03x-%03x-%x\n",
id.manufacturer_id, id.part_id,
id.die_revision);
return -ENODEV;
}
}
data->idle_state = MUX_IDLE_AS_IS;
if (device_property_read_u32(dev, "idle-state", &data->idle_state)) {
if (device_property_read_bool(dev, "i2c-mux-idle-disconnect"))
data->idle_state = MUX_IDLE_DISCONNECT;
}
/*
* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to a channel
* or disconnected state.
*/
ret = pca954x_init(client, data);
if (ret < 0) {
dev_warn(dev, "probe failed\n");
return -ENODEV;
}
ret = pca954x_irq_setup(muxc);
if (ret)
goto fail_cleanup;
/* Now create an adapter for each channel */
for (num = 0; num < data->chip->nchans; num++) {
force = 0; /* dynamic adap number */
class = 0; /* no class by default */
if (pdata) {
if (num < pdata->num_modes) {
/* force static number */
force = pdata->modes[num].adap_id;
class = pdata->modes[num].class;
} else
/* discard unconfigured channels */
break;
}
ret = i2c_mux_add_adapter(muxc, force, num, class);
if (ret)
goto fail_cleanup;
}
if (data->irq) {
ret = devm_request_threaded_irq(dev, data->client->irq,
NULL, pca954x_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
"pca954x", data);
if (ret)
goto fail_cleanup;
}
/*
* The attr probably isn't going to be needed in most cases,
* so don't fail completely on error.
*/
device_create_file(dev, &dev_attr_idle_state);
dev_info(dev, "registered %d multiplexed busses for I2C %s %s\n",
num, data->chip->muxtype == pca954x_ismux
? "mux" : "switch", client->name);
return 0;
fail_cleanup:
pca954x_cleanup(muxc);
return ret;
}
static int pca954x_remove(struct i2c_client *client)
{
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
device_remove_file(&client->dev, &dev_attr_idle_state);
pca954x_cleanup(muxc);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int pca954x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
struct pca954x *data = i2c_mux_priv(muxc);
int ret;
ret = pca954x_init(client, data);
if (ret < 0)
dev_err(&client->dev, "failed to verify mux presence\n");
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume);
static struct i2c_driver pca954x_driver = {
.driver = {
.name = "ctc_pca954x",
.pm = &pca954x_pm,
.of_match_table = pca954x_of_match,
},
.probe = pca954x_probe,
.remove = pca954x_remove,
.id_table = pca954x_id,
};
module_i2c_driver(pca954x_driver);
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
MODULE_DESCRIPTION("PCA954x I2C mux/switch driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,48 @@
/*
*
* pca954x.h - I2C multiplexer/switch support
*
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti@xxxxxxxx>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info@xxxxxxxxxxx>
* Michael Lawnick <michael.lawnick.ext@xxxxxxx>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _LINUX_I2C_PCA954X_H
#define _LINUX_I2C_PCA954X_H
/* Platform data for the PCA954x I2C multiplexers */
/* Per channel initialisation data:
* @adap_id: bus number for the adapter. 0 = don't care
* @deselect_on_exit: set this entry to 1, if your H/W needs deselection
* of this channel after transaction.
*
*/
struct pca954x_platform_mode {
int adap_id;
unsigned int deselect_on_exit:1;
unsigned int class;
};
/* Per mux/switch data, used with i2c_register_board_info */
struct pca954x_platform_data {
struct pca954x_platform_mode *modes;
int num_modes;
};
#endif /* _LINUX_I2C_PCA954X_H */

View File

@ -7,6 +7,6 @@ Standards-Version: 3.9.3
Package: platform-modules-embedway-es6220 Package: platform-modules-embedway-es6220
Architecture: amd64 Architecture: amd64
Depends: linux-image-4.19.0-12-2-amd64-unsigned Depends: linux-image-5.10.0-8-2-amd64-unsigned
Description: kernel modules for platform devices such as fan, led, sfp Description: kernel modules for platform devices such as fan, led, sfp

View File

@ -20,12 +20,16 @@ generate_version_file()
if [ "$ENABLE_VERSION_CONTROL_DEB" != "y" ]; then if [ "$ENABLE_VERSION_CONTROL_DEB" != "y" ]; then
if [[ $CONFIGURED_ARCH == armhf || $CONFIGURED_ARCH == arm64 ]]; then if [[ $CONFIGURED_ARCH == armhf || $CONFIGURED_ARCH == arm64 ]]; then
if [ $MULTIARCH_QEMU_ENVIRON == y ]; then if [ $MULTIARCH_QEMU_ENVIRON == "y" ]; then
# qemu arm bin executable for cross-building # qemu arm bin executable for cross-building
sudo mkdir -p $FILESYSTEM_ROOT/usr/bin sudo mkdir -p $FILESYSTEM_ROOT/usr/bin
sudo cp /usr/bin/qemu*static $FILESYSTEM_ROOT/usr/bin || true sudo cp /usr/bin/qemu*static $FILESYSTEM_ROOT/usr/bin || true
fi fi
sudo http_proxy=$HTTP_PROXY SKIP_BUILD_HOOK=y debootstrap --variant=minbase --arch $CONFIGURED_ARCH $IMAGE_DISTRO $FILESYSTEM_ROOT http://deb.debian.org/debian sudo http_proxy=$HTTP_PROXY SKIP_BUILD_HOOK=y debootstrap --foreign --variant=minbase --arch $CONFIGURED_ARCH $IMAGE_DISTRO $FILESYSTEM_ROOT http://deb.debian.org/debian
sudo rm $FILESYSTEM_ROOT/proc -rf
sudo mkdir $FILESYSTEM_ROOT/proc
sudo mount -t proc proc $FILESYSTEM_ROOT/proc
sudo LANG=C chroot $FILESYSTEM_ROOT /debootstrap/debootstrap --second-stage
else else
sudo http_proxy=$HTTP_PROXY SKIP_BUILD_HOOK=y debootstrap --variant=minbase --arch $CONFIGURED_ARCH $IMAGE_DISTRO $FILESYSTEM_ROOT http://debian-archive.trafficmanager.net/debian sudo http_proxy=$HTTP_PROXY SKIP_BUILD_HOOK=y debootstrap --variant=minbase --arch $CONFIGURED_ARCH $IMAGE_DISTRO $FILESYSTEM_ROOT http://debian-archive.trafficmanager.net/debian
fi fi

View File

@ -3,7 +3,9 @@ FROM multiarch/qemu-user-static:x86_64-arm-5.0.0-2 as qemu
FROM multiarch/debian-debootstrap:armhf-bullseye FROM multiarch/debian-debootstrap:armhf-bullseye
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
{%- elif CONFIGURED_ARCH == "arm64" and MULTIARCH_QEMU_ENVIRON == "y" %} {%- elif CONFIGURED_ARCH == "arm64" and MULTIARCH_QEMU_ENVIRON == "y" %}
FROM multiarch/qemu-user-static:x86_64-aarch64-5.2.0-2 as qemu
FROM multiarch/debian-debootstrap:arm64-bullseye FROM multiarch/debian-debootstrap:arm64-bullseye
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
{%- else -%} {%- else -%}
FROM debian:bullseye FROM debian:bullseye
{%- endif %} {%- endif %}
@ -351,6 +353,11 @@ RUN apt-get update && apt-get install -y \
RUN apt-get -y build-dep openssh RUN apt-get -y build-dep openssh
# Build fix for ARM64 and ARMHF /etc/debian_version
{%- if CONFIGURED_ARCH == "armhf" or CONFIGURED_ARCH == "arm64" %}
RUN apt upgrade -y base-files
{%- endif %}
# Build fix for ARMHF bullseye libsairedis # Build fix for ARMHF bullseye libsairedis
{%- if CONFIGURED_ARCH == "armhf" %} {%- if CONFIGURED_ARCH == "armhf" %}
# Install doxygen build dependency packages # Install doxygen build dependency packages