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:
parent
b4dda1c18d
commit
5b284767f6
@ -1,13 +1,13 @@
|
||||
## Debian mirror for ARM
|
||||
## 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-src [arch=arm64] http://deb.debian.org/debian buster main contrib non-free
|
||||
deb [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free
|
||||
deb-src [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free
|
||||
deb [arch=arm64] http://ftp.debian.org/debian buster-backports main
|
||||
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster main contrib non-free
|
||||
deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian buster main contrib non-free
|
||||
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free
|
||||
deb-src [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free
|
||||
deb [arch=arm64] http://packages.trafficmanager.net/debian/debian buster-backports main
|
||||
deb [arch=arm64] http://deb.debian.org/debian bullseye 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 bullseye-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 bullseye-backports main
|
||||
# deb [arch=arm64] http://packages.trafficmanager.net/debian/debian bullseye 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 bullseye-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 bullseye-backports main
|
||||
|
@ -1,13 +1,13 @@
|
||||
## Debian mirror for ARM
|
||||
## 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-src [arch=armhf] http://deb.debian.org/debian buster main contrib non-free
|
||||
deb [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free
|
||||
deb-src [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free
|
||||
deb [arch=armhf] http://ftp.debian.org/debian buster-backports main
|
||||
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster main contrib non-free
|
||||
deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian buster main contrib non-free
|
||||
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free
|
||||
deb-src [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-updates main contrib non-free
|
||||
deb [arch=armhf] http://packages.trafficmanager.net/debian/debian buster-backports main
|
||||
deb [arch=armhf] http://deb.debian.org/debian bullseye 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 bullseye-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 bullseye-backports main
|
||||
# deb [arch=armhf] http://packages.trafficmanager.net/debian/debian bullseye 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 bullseye-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 bullseye-backports main
|
||||
|
@ -643,7 +643,7 @@ clean_proc() {
|
||||
sudo umount /proc || true
|
||||
}
|
||||
trap_push clean_proc
|
||||
sudo mount proc /proc -t proc
|
||||
sudo mount proc /proc -t proc || true
|
||||
sudo mkdir $FILESYSTEM_ROOT/target
|
||||
sudo mount --bind target $FILESYSTEM_ROOT/target
|
||||
sudo LANG=C DOCKER_HOST="$DOCKER_HOST" chroot $FILESYSTEM_ROOT docker info
|
||||
|
@ -1,9 +1,9 @@
|
||||
deb [arch=arm64] http://deb.debian.org/debian buster main contrib non-free
|
||||
deb-src [arch=arm64] http://deb.debian.org/debian buster main contrib non-free
|
||||
deb [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free
|
||||
deb-src [arch=arm64] http://deb.debian.org/debian buster-updates main contrib non-free
|
||||
deb [arch=arm64] http://security.debian.org buster/updates main contrib non-free
|
||||
deb-src [arch=arm64] http://security.debian.org buster/updates main contrib non-free
|
||||
deb [arch=arm64] https://download.docker.com/linux/debian buster stable
|
||||
deb [arch=arm64] http://ftp.debian.org/debian buster-backports main
|
||||
deb [arch=arm64] http://deb.debian.org/debian bullseye 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 bullseye-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 bullseye-security 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 bullseye stable
|
||||
deb [arch=arm64] http://ftp.debian.org/debian bullseye-backports main
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
deb [arch=armhf] http://deb.debian.org/debian buster main contrib non-free
|
||||
deb-src [arch=armhf] http://deb.debian.org/debian buster main contrib non-free
|
||||
deb [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free
|
||||
deb-src [arch=armhf] http://deb.debian.org/debian buster-updates main contrib non-free
|
||||
deb [arch=armhf] http://security.debian.org buster/updates main contrib non-free
|
||||
deb-src [arch=armhf] http://security.debian.org buster/updates main contrib non-free
|
||||
deb [arch=armhf] https://download.docker.com/linux/debian buster stable
|
||||
deb [arch=armhf] http://ftp.debian.org/debian buster-backports main
|
||||
deb [arch=armhf] http://deb.debian.org/debian bullseye 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 bullseye-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 bullseye-security 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 bullseye stable
|
||||
deb [arch=armhf] http://ftp.debian.org/debian bullseye-backports main
|
||||
|
||||
|
@ -10,7 +10,6 @@ rtc-sd2405
|
||||
ctc5236_switch
|
||||
ctc5236_mdio
|
||||
ctcmac
|
||||
ctcmac_test
|
||||
ctc5236-mc
|
||||
ctc_wdt
|
||||
ehci-ctc
|
||||
@ -20,3 +19,4 @@ pwm-ctc
|
||||
ext4
|
||||
overlay
|
||||
squashfs
|
||||
mars
|
||||
|
@ -1 +1,3 @@
|
||||
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
|
||||
|
||||
obj-m := centec_e530_24x2c_platform.o
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/pca954x.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/leds.h>
|
||||
@ -129,7 +128,7 @@ static int e530_24x2c_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio0 = NULL;
|
||||
@ -145,7 +144,7 @@ static int e530_24x2c_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio1 = NULL;
|
||||
@ -161,7 +160,7 @@ static int e530_24x2c_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio2 = NULL;
|
||||
|
@ -1 +1,3 @@
|
||||
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
|
||||
|
||||
obj-m := centec_e530_24x2q_platform.o
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/pca954x.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/leds.h>
|
||||
@ -129,7 +128,7 @@ static int e530_24x2q_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio0 = NULL;
|
||||
@ -145,7 +144,7 @@ static int e530_24x2q_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio1 = NULL;
|
||||
@ -161,7 +160,7 @@ static int e530_24x2q_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio2 = NULL;
|
||||
|
@ -1 +1,3 @@
|
||||
KBUILD_EXTRA_SYMBOLS = /sonic/platform/centec-arm64/sonic-platform-modules-e530/pca954x/Module.symvers
|
||||
|
||||
obj-m := centec_e530_48s4x_platform.o
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/pca954x.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/leds.h>
|
||||
@ -197,7 +196,7 @@ static int e530_48s4x_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio0 = NULL;
|
||||
@ -213,7 +212,7 @@ static int e530_48s4x_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio1 = NULL;
|
||||
@ -274,7 +273,7 @@ static int e530_48s4x_init_i2c_epld(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_epld = NULL;
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/pca954x.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/leds.h>
|
||||
@ -195,7 +194,7 @@ static int e530_48t4x_p_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio0 = NULL;
|
||||
|
@ -7,20 +7,20 @@ Standards-Version: 3.9.3
|
||||
|
||||
Package: platform-modules-e530-48t4x-p
|
||||
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
|
||||
|
||||
Package: platform-modules-e530-24x2c
|
||||
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
|
||||
|
||||
Package: platform-modules-e530-48s4x
|
||||
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
|
||||
|
||||
Package: platform-modules-e530-24x2q
|
||||
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
|
||||
|
@ -17,10 +17,6 @@ function install_python_api_package()
|
||||
device="/usr/share/sonic/device"
|
||||
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)
|
||||
if [ $? -ne 0 ]; then
|
||||
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
|
||||
fi
|
||||
depmod -a
|
||||
modprobe ctc-i2c-mux-pca954x
|
||||
modprobe centec_e530_24x2c_platform
|
||||
modprobe fan-ctc5236
|
||||
modprobe dal
|
||||
@ -48,6 +45,7 @@ function remove_kernel_modules()
|
||||
modprobe -r dal
|
||||
modprobe -r fan-ctc5236
|
||||
modprobe -r centec_e530_24x2c_platform
|
||||
modprobe -r ctc-i2c-mux-pca954x
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
|
@ -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
|
||||
|
@ -17,10 +17,6 @@ function install_python_api_package()
|
||||
device="/usr/share/sonic/device"
|
||||
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)
|
||||
if [ $? -ne 0 ]; then
|
||||
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
|
||||
fi
|
||||
depmod -a
|
||||
modprobe ctc-i2c-mux-pca954x
|
||||
modprobe centec_e530_24x2q_platform
|
||||
modprobe fan-ctc5236
|
||||
modprobe dal
|
||||
@ -48,6 +45,7 @@ function remove_kernel_modules()
|
||||
modprobe -r dal
|
||||
modprobe -r fan-ctc5236
|
||||
modprobe -r centec_e530_24x2q_platform
|
||||
modprobe -r ctc-i2c-mux-pca954x
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
|
@ -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
|
||||
|
@ -17,10 +17,6 @@ function install_python_api_package()
|
||||
device="/usr/share/sonic/device"
|
||||
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)
|
||||
if [ $? -ne 0 ]; then
|
||||
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
|
||||
fi
|
||||
depmod -a
|
||||
modprobe ctc-i2c-mux-pca954x
|
||||
modprobe centec_e530_48s4x_platform
|
||||
modprobe fan-ctc5236
|
||||
modprobe dal
|
||||
@ -48,6 +45,7 @@ function remove_kernel_modules()
|
||||
modprobe -r dal
|
||||
modprobe -r fan-ctc5236
|
||||
modprobe -r centec_e530_48s4x_platform
|
||||
modprobe -r ctc-i2c-mux-pca954x
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
|
@ -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
|
||||
|
@ -17,10 +17,6 @@ function install_python_api_package()
|
||||
device="/usr/share/sonic/device"
|
||||
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)
|
||||
if [ $? -ne 0 ]; then
|
||||
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
|
||||
fi
|
||||
depmod -a
|
||||
modprobe ctc-i2c-mux-pca954x
|
||||
modprobe centec_e530_48t4x_p_platform
|
||||
modprobe fan-ctc5236
|
||||
modprobe dal
|
||||
@ -48,6 +45,7 @@ function remove_kernel_modules()
|
||||
modprobe -r dal
|
||||
modprobe -r fan-ctc5236
|
||||
modprobe -r centec_e530_48t4x_p_platform
|
||||
modprobe -r ctc-i2c-mux-pca954x
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
|
@ -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
|
||||
|
@ -24,6 +24,7 @@ CLASSES_DIR := classes
|
||||
CONF_DIR := conf
|
||||
KDAL_DIR := ../../centec/centec-dal/
|
||||
FAN_DIR := fan
|
||||
PCA954X_DIR := pca954x
|
||||
|
||||
%:
|
||||
dh $@
|
||||
@ -41,10 +42,12 @@ build:
|
||||
(for mod in $(FAN_DIR); do \
|
||||
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/; \
|
||||
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 \
|
||||
make modules -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
|
||||
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; \
|
||||
cd -; \
|
||||
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)/$(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)/$(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/; \
|
||||
done)
|
||||
# Resuming debhelper scripts
|
||||
|
@ -0,0 +1 @@
|
||||
obj-m := ctc-i2c-mux-pca954x.o
|
@ -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");
|
@ -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 */
|
@ -12,7 +12,7 @@
|
||||
images {
|
||||
kernel_ctc {
|
||||
description = "ARM64 Kernel";
|
||||
data = /incbin/("./vmlinuz-4.19.0-12-2-arm64");
|
||||
data = /incbin/("./vmlinuz-5.10.0-8-2-arm64");
|
||||
type = "kernel";
|
||||
arch = "arm64";
|
||||
os = "linux";
|
||||
@ -25,7 +25,7 @@
|
||||
};
|
||||
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";
|
||||
arch = "arm64";
|
||||
os = "linux";
|
||||
|
@ -7,5 +7,5 @@ Standards-Version: 3.9.3
|
||||
|
||||
Package: tsingma-bsp
|
||||
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
|
||||
|
2
platform/centec-arm64/tsingma-bsp/debian/rules
Normal file → Executable file
2
platform/centec-arm64/tsingma-bsp/debian/rules
Normal file → Executable file
@ -15,7 +15,7 @@ PACKAGE_PRE_NAME := tsingma-bsp
|
||||
KVERSION ?= $(shell uname -r)
|
||||
KERNEL_SRC := /lib/modules/$(KVERSION)
|
||||
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
|
||||
MODULE_DIR := src
|
||||
UTILS_DIR := utils
|
||||
|
@ -1,17 +1,17 @@
|
||||
src/ctc5236-mc/ctc5236-mc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/pwm-ctc/pwm-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/ctc5236_switch/ctc5236_switch.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/pinctrl-ctc/pinctrl-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/ctc_wdt/ctc_wdt.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/ctcmac/ctcmac.ko /lib/modules/4.19.0-12-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/4.19.0-12-2-arm64/kernel/extra
|
||||
src/i2c-ctc/i2c-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/gpio-ctc/gpio-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/ehci-ctc/ehci-ctc.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/rtc-sd2405/rtc-sd2405.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/sdhci-ctc5236/sdhci-ctc5236.ko /lib/modules/4.19.0-12-2-arm64/kernel/extra
|
||||
src/spi-ctc-qspi/spi-ctc-qspi.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/5.10.0-8-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/5.10.0-8-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/5.10.0-8-2-arm64/kernel/extra
|
||||
src/ctcmac/ctc5236_mdio.ko /lib/modules/5.10.0-8-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/5.10.0-8-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/5.10.0-8-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/5.10.0-8-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/config/fw_env.config /etc/
|
||||
src/config/tsingma-bsp.service /lib/systemd/system
|
||||
|
1
platform/centec-arm64/tsingma-bsp/src/ctc-dts/arm-gic.h
Normal file → Executable file
1
platform/centec-arm64/tsingma-bsp/src/ctc-dts/arm-gic.h
Normal file → Executable file
@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* This header provides constants for the ARM GIC.
|
||||
*/
|
||||
|
66
platform/centec-arm64/tsingma-bsp/src/ctc-dts/ctc5236.dtsi
Normal file → Executable file
66
platform/centec-arm64/tsingma-bsp/src/ctc-dts/ctc5236.dtsi
Normal file → Executable file
@ -88,7 +88,11 @@
|
||||
memory-controller@30600000 {
|
||||
compatible = "ctc,ctc5236-ddr-ctrl";
|
||||
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 {
|
||||
@ -136,12 +140,15 @@
|
||||
#size-cells = <2>;
|
||||
interrupt-parent = <&gic>;
|
||||
status = "disabled";
|
||||
local-mac-address = [00 00 00 00 00 00];
|
||||
index = <0x00>;
|
||||
reg = <0x0 0x33410000 0x0 0x10000>,
|
||||
<0x0 0x33400000 0x0 0x10000>;
|
||||
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 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>;
|
||||
};
|
||||
|
||||
@ -152,12 +159,15 @@
|
||||
#size-cells = <2>;
|
||||
interrupt-parent = <&gic>;
|
||||
status = "disabled";
|
||||
local-mac-address = [00 00 00 00 00 00];
|
||||
index = <0x01>;
|
||||
reg = <0x0 0x33420000 0x0 0x10000>,
|
||||
<0x0 0x33400000 0x0 0x10000>;
|
||||
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 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>;
|
||||
};
|
||||
|
||||
@ -165,6 +175,7 @@
|
||||
compatible = "ctc-ehci";
|
||||
reg = <0x0 0x30500000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
|
||||
ctc,sysctrl = <&sysctrl>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -172,7 +183,7 @@
|
||||
compatible = "generic-ohci";
|
||||
reg = <0x0 0x30580000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
|
||||
status = "disabled";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
spi: spi@33100000 {
|
||||
@ -188,7 +199,7 @@
|
||||
status ="disabled";
|
||||
};
|
||||
|
||||
qspi: qspi@10000000 {
|
||||
qspi: spi@10000000 {
|
||||
compatible = "ctc, igdaxi001a-qspi";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -228,6 +239,8 @@
|
||||
reg = <0x0 0x33700000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&i2c_clk>;
|
||||
ctc,sysctrl = <&sysctrl>;
|
||||
i2c-num = <0>;
|
||||
status ="disabled";
|
||||
};
|
||||
|
||||
@ -238,6 +251,8 @@
|
||||
reg = <0x0 0x33701000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&i2c_clk>;
|
||||
ctc,sysctrl = <&sysctrl>;
|
||||
i2c-num = <1>;
|
||||
status ="disabled";
|
||||
};
|
||||
|
||||
@ -257,7 +272,8 @@
|
||||
interrupt-names = "msi","aer","pme";
|
||||
msi-parent = <&pcie>;
|
||||
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>;
|
||||
ctc,sysctrl = <&sysctrl>;
|
||||
status ="disabled";
|
||||
@ -338,6 +354,7 @@
|
||||
reg = <0x0 0x33610000 0x0 0x10000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
ctc,sysctrl = <&sysctrl>;
|
||||
|
||||
porta: gpio-port@0 {
|
||||
compatible = "ctc,apb-gpio-porta";
|
||||
@ -368,7 +385,42 @@
|
||||
ctc,pinctrl-bank0 = <16>;
|
||||
ctc,pinctrl-bank1 = <8>;
|
||||
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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -157,6 +157,7 @@
|
||||
non-removable;
|
||||
no-sd;
|
||||
no-sdio;
|
||||
cap-mmc-hw-reset;
|
||||
voltage-ranges = <3300 3300>;
|
||||
status = "okay";
|
||||
};
|
||||
@ -187,7 +188,7 @@
|
||||
|
||||
|
||||
&pinctrl {
|
||||
spi {
|
||||
spi0 {
|
||||
spi_pin: spi_pin {
|
||||
status = "disabled";
|
||||
ctc,pins = <0 0 PIN_FUNC_SPI>,
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
|
||||
/*
|
||||
* This header provides constants for most IRQ bindings.
|
||||
*
|
||||
|
1
platform/centec-arm64/tsingma-bsp/src/ctc-phy/Makefile
Normal file
1
platform/centec-arm64/tsingma-bsp/src/ctc-phy/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-m = mars.o
|
316
platform/centec-arm64/tsingma-bsp/src/ctc-phy/mars.c
Normal file
316
platform/centec-arm64/tsingma-bsp/src/ctc-phy/mars.c
Normal 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);
|
230
platform/centec-arm64/tsingma-bsp/src/ctc5236-mc/ctc5236-mc.c
Normal file → Executable file
230
platform/centec-arm64/tsingma-bsp/src/ctc5236-mc/ctc5236-mc.c
Normal file → Executable file
@ -1,4 +1,5 @@
|
||||
/* Centec TsingMa Memory Controller Driver
|
||||
/*
|
||||
* Centec TsingMa Memory Contoller Driver
|
||||
*
|
||||
* Author: lius <lius@centecnetworks.com>
|
||||
*
|
||||
@ -22,12 +23,19 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include "../include/sysctl.h"
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
struct ctc5236_mc {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
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 */
|
||||
@ -45,6 +53,7 @@ struct ctc5236_mc {
|
||||
#define DDR_PORT2_ERR_INT_STATUS 0x3
|
||||
#define DDR_PORT3_ERR_INT_STATUS 0x4
|
||||
#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_OVERFLOW 0x40000
|
||||
#define DDR_ERR_RD_PORT_REC_UNDERFLOW 0x80000
|
||||
@ -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)
|
||||
);
|
||||
|
||||
pr_emerg("ERROR:port%d is out of top-bound range!\n"
|
||||
"The error address is 0x%p\n",
|
||||
printk(KERN_EMERG
|
||||
"ERROR:port%d is out of top-bound range!\n The error address is 0x%p\n",
|
||||
id, (void *)temp);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WCMDQ_OVER)
|
||||
pr_err("ERROR:port%d write command queue is overflow!\n", id);
|
||||
if (status & DDR_ERR_WCMDQ_OVER) {
|
||||
printk(KERN_ERR
|
||||
"ERROR:port%d write command queue is overflow!\n", id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WCMDQ_UNDER)
|
||||
pr_err("ERROR:port%d write command queue is underflow!\n", id);
|
||||
if (status & DDR_ERR_WCMDQ_UNDER) {
|
||||
printk(KERN_ERR
|
||||
"ERROR:port%d write command queue is underflow!\n", id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WDATAQ_OVER)
|
||||
pr_err("ERROR:port%d write data queue is overflow!\n", id);
|
||||
if (status & DDR_ERR_WDATAQ_OVER) {
|
||||
printk(KERN_ERR "ERROR:port%d write data queue is overflow!\n",
|
||||
id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WDATAQ_UNDER)
|
||||
pr_err("ERROR:port%d write data queue is underflow!\n", id);
|
||||
if (status & DDR_ERR_WDATAQ_UNDER) {
|
||||
printk(KERN_ERR "ERROR:port%d write data queue is underflow!\n",
|
||||
id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WESPQ_OVER)
|
||||
pr_err("ERROR:port%d write response queue is overflow!\n", id);
|
||||
if (status & DDR_ERR_WESPQ_OVER) {
|
||||
printk(KERN_ERR
|
||||
"ERROR:port%d write response queue is overflow!\n", id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WESPQ_UNDER)
|
||||
pr_err("ERROR:port%d write response queue is underflow!\n", id);
|
||||
if (status & DDR_ERR_WESPQ_UNDER) {
|
||||
printk(KERN_ERR
|
||||
"ERROR:port%d write response queue is underflow!\n", id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WINFOQ_OVER)
|
||||
pr_err("ERROR:port%d write info queue is overflow!\n", id);
|
||||
if (status & DDR_ERR_WINFOQ_OVER) {
|
||||
printk(KERN_ERR "ERROR:port%d write info queue is overflow!\n",
|
||||
id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WINFOQ_UNDER)
|
||||
pr_err("ERROR:port%d write info queue is underflow!\n", id);
|
||||
if (status & DDR_ERR_WINFOQ_UNDER) {
|
||||
printk(KERN_ERR "ERROR:port%d write info queue is underflow!\n",
|
||||
id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_RCMDQ_OVER)
|
||||
pr_err("ERROR:port%d read command queue is overflow!\n", id);
|
||||
if (status & DDR_ERR_RCMDQ_OVER) {
|
||||
printk(KERN_ERR
|
||||
"ERROR:port%d read command queue is overflow!\n", id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_RCMDQ_UNDER)
|
||||
pr_err("ERROR:port%d read command queue is underflow!\n", id);
|
||||
if (status & DDR_ERR_RCMDQ_UNDER) {
|
||||
printk(KERN_ERR
|
||||
"ERROR:port%d read command queue is underflow!\n", id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_RDATAQ_OVER)
|
||||
pr_err("ERROR:port%d read data queue is overflow!\n", id);
|
||||
if (status & DDR_ERR_RDATAQ_OVER) {
|
||||
printk(KERN_ERR "ERROR:port%d read data queue is overflow!\n",
|
||||
id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_RDATAQ_UNDER)
|
||||
pr_err("ERROR:port%d read data queue is underflow!\n", id);
|
||||
if (status & DDR_ERR_RDATAQ_UNDER) {
|
||||
printk(KERN_ERR "ERROR:port%d read data queue is underflow!\n",
|
||||
id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_RESPQ_OVER)
|
||||
pr_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_OVER) {
|
||||
printk(KERN_ERR
|
||||
"ERROR:port%d read response queue is overflow!\n", id);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_RESPQ_UNDER) {
|
||||
printk(KERN_ERR
|
||||
"ERROR:port%d read response queue is underflow!\n", id);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_ECC_INT_STATUS)
|
||||
pr_err("ERROR:The ecc more than 1-bit error !\n");
|
||||
if (status & DDR_ERR_ECC_INT_STATUS) {
|
||||
printk(KERN_ERR "ERROR:The ecc more than 1-bit error !\n");
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WR_PORT_REC_UNDERFLOW)
|
||||
pr_err("ERROR:MPARB wr_port_rec FIFO is underflow!\n");
|
||||
if (status & DDR_ERR_WR_PORT_REC_UNDERFLOW) {
|
||||
printk(KERN_ERR "ERROR:MPARB wr_port_rec FIFO is underflow!\n");
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_WR_PORT_REC_OVERFLOW)
|
||||
pr_err("ERROR:MPARB wr_port_rec FIFO is overflow!\n");
|
||||
if (status & DDR_ERR_WR_PORT_REC_OVERFLOW) {
|
||||
printk(KERN_ERR "ERROR:MPARB wr_port_rec FIFO is overflow!\n");
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_RD_PORT_REC_UNDERFLOW)
|
||||
pr_err("ERROR:MPARB rd_port_rec FIFO is underflow!\n");
|
||||
if (status & DDR_ERR_RD_PORT_REC_UNDERFLOW) {
|
||||
printk(KERN_ERR "ERROR:MPARB rd_port_rec FIFO is underflow!\n");
|
||||
}
|
||||
|
||||
if (status & DDR_ERR_RD_PORT_REC_OVERFLOW)
|
||||
pr_err("ERROR:MPARB rd_port_rec FIFO is underflow!\n");
|
||||
if (status & DDR_ERR_RD_PORT_REC_OVERFLOW) {
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
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[] = {
|
||||
{
|
||||
.compatible = "ctc,ctc5236-ddr-ctrl",
|
||||
@ -212,6 +294,7 @@ static int ctc5236_mc_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *id;
|
||||
struct ctc5236_mc *mci;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
id = of_match_device(ctc5236_ddr_ctrl_of_match, &pdev->dev);
|
||||
if (!id)
|
||||
@ -225,12 +308,67 @@ static int ctc5236_mc_probe(struct platform_device *pdev)
|
||||
if (IS_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);
|
||||
ret =
|
||||
devm_request_irq(&pdev->dev, mci->irq, ctc_mc_err_handler, 0,
|
||||
dev_name(&pdev->dev), mci);
|
||||
"DDR Ecc", mci);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -276,4 +414,4 @@ module_exit(ctc5236_mc_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Centec Network");
|
||||
MODULE_DESCRIPTION("Centec TsingMa memory controller driver");
|
||||
MODULE_DESCRIPTION("Centec TsingMa memory contoller driver");
|
||||
|
117
platform/centec-arm64/tsingma-bsp/src/ctc5236_switch/ctc5236_switch.c
Normal file → Executable file
117
platform/centec-arm64/tsingma-bsp/src/ctc5236_switch/ctc5236_switch.c
Normal file → Executable 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>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
@ -9,15 +10,15 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.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
|
||||
|
||||
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 cmd_len = 0;
|
||||
u8 index = 0;
|
||||
@ -28,7 +29,7 @@ int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
|
||||
}
|
||||
|
||||
/* 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);
|
||||
return -1;
|
||||
}
|
||||
@ -39,22 +40,23 @@ int ctc5236_switch_read(u32 offset, u32 len, u32 *p_value)
|
||||
cmd_len = len;
|
||||
do {
|
||||
cmd_len++;
|
||||
} while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
|
||||
}
|
||||
while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* normal operate only support 1 entry */
|
||||
cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len;
|
||||
cmd_status_u.cmd_status.cmdEntryWords = (len == 16) ? 0 : len; /* normal operate only support 1 entry */
|
||||
cmd_status_u.cmd_status.cmdDataLen = len;
|
||||
writel(cmd_status_u.val, &access->cmd_status);
|
||||
/* 2. write AddrReg */
|
||||
writel(offset, &access->addr);
|
||||
/* 3. polling status and check */
|
||||
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);
|
||||
}
|
||||
/* 4. check cmd done */
|
||||
if (!(cmd_status_u.cmd_status.reqProcDone)) {
|
||||
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 */
|
||||
for (index = 0; index < len; index++)
|
||||
for (index = 0; index < len; index++) {
|
||||
p_value[index] = readl(&access->data[index]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 cmd_len = 0;
|
||||
u8 index = 0;
|
||||
@ -88,7 +91,7 @@ int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
|
||||
}
|
||||
|
||||
/* 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);
|
||||
return -1;
|
||||
}
|
||||
@ -100,28 +103,28 @@ int ctc5236_switch_write(u32 offset, u32 len, u32 *p_value)
|
||||
cmd_len = len;
|
||||
do {
|
||||
cmd_len++;
|
||||
} while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
|
||||
}
|
||||
while ((cmd_len <= 16) && (cmd_len & (cmd_len - 1)));
|
||||
}
|
||||
|
||||
/* 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.cmdEntryWords = (len == 16) ? 0 : len;
|
||||
/* Notice: for 1 entry op, cmdDatalen eq cmdEntryWords,
|
||||
* but for mutil entry, len = cmd_len
|
||||
*/
|
||||
cmd_status_u.cmd_status.cmdDataLen = len;
|
||||
cmd_status_u.cmd_status.cmdDataLen = len; /* Notice: for 1 entry op, cmdDatalen eq cmdEntryWords, but for mutil entry, len = cmd_len */
|
||||
writel(cmd_status_u.val, &access->cmd_status);
|
||||
/* 2. write AddrReg */
|
||||
writel(offset, &access->addr);
|
||||
/* 3. write data into databuffer */
|
||||
for (index = 0; index < len; index++)
|
||||
for (index = 0; index < len; index++) {
|
||||
writel(p_value[index], &access->data[index]);
|
||||
}
|
||||
|
||||
/* 4. polling status and check */
|
||||
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);
|
||||
}
|
||||
|
||||
/* 5. check cmd done */
|
||||
if (!(cmd_status_u.cmd_status.reqProcDone)) {
|
||||
@ -144,26 +147,26 @@ static int
|
||||
_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] = {
|
||||
804, 801, 798, 795, 792, 790, 787, 784, 781, 778,
|
||||
775, 772, 769, 766, 763, 761, 758, 755, 752, 749,
|
||||
746, 743, 740, 737, 734, 731, 728, 725, 722, 719,
|
||||
717, 714, 711, 708, 705, 702, 699, 696, 693, 690,
|
||||
687, 684, 681, 678, 675, 672, 669, 666, 663, 660,
|
||||
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,
|
||||
569, 566, 563, 560, 557, 554, 551, 548, 545, 542,
|
||||
540, 537, 534, 531, 528, 525, 522, 519, 516, 513,
|
||||
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,
|
||||
804, 801, 798, 795, 792, 790, 787, 784, 781, 778, 775, 772, 769, 766, 763, 761, 758, 755, 752, 749,
|
||||
/*-40~-21*/
|
||||
746, 743, 740, 737, 734, 731, 728, 725, 722, 719, 717, 714, 711, 708, 705, 702, 699, 696, 693, 690,
|
||||
/*-20~-1*/
|
||||
687, 684, 681, 678, 675, 672, 669, 666, 663, 660, 658, 655, 652, 649, 646, 643, 640, 637, 634, 631, /*0~19 */
|
||||
628, 625, 622, 619, 616, 613, 610, 607, 604, 601, 599, 596, 593, 590, 587, 584, 581, 578, 575, 572, /*20~39 */
|
||||
569, 566, 563, 560, 557, 554, 551, 548, 545, 542, 540, 537, 534, 531, 528, 525, 522, 519, 516, 513, /*40~59 */
|
||||
510, 507, 504, 501, 498, 495, 492, 489, 486, 483, 481, 478, 475, 472, 469, 466, 463, 460, 457, 454, /*60~79 */
|
||||
451, 448, 445, 442, 439, 436, 433, 430, 427, 424, 421, 418, 415, 412, 409, 406, 403, 400, 397, 394, /*80~99 */
|
||||
391, 388, 385, 382, 379, 376, 373, 370, 367, 364, 361, 358, 355, 352, 349, 346, 343, 340, 337, 334, /*100~119 */
|
||||
331, 328, 325, 322, 319, 316, 0
|
||||
};
|
||||
}; /*120~125 */
|
||||
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++) {
|
||||
if ((temp_code <= temp_mapping_tbl[index])
|
||||
&& (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);
|
||||
else
|
||||
} else {
|
||||
*p_temp_val = index - 40;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -189,27 +193,6 @@ int get_switch_temperature(void)
|
||||
offset = 0xf * 4;
|
||||
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 */
|
||||
/*mask_write tbl-reg OmcMem 0x11 offset 0x0 0x02000007 0x03000007 */
|
||||
offset = 0x11 * 4;
|
||||
@ -237,6 +220,8 @@ int get_switch_temperature(void)
|
||||
value |= BIT(0);
|
||||
ctc5236_switch_write(OMCMEM_BASE + offset, 1, &value);
|
||||
|
||||
msleep(1);
|
||||
|
||||
/*mask_write tbl-reg OmcMem 0x10 offset 0x0 0x00000001 0x00000001 */
|
||||
offset = 0x10 * 4;
|
||||
ctc5236_switch_read(OMCMEM_BASE + offset, 1, &value);
|
||||
@ -262,8 +247,9 @@ int get_switch_temperature(void)
|
||||
|
||||
msleep(1);
|
||||
}
|
||||
if (timeout == 0)
|
||||
if (0 == timeout) {
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
/*mask_write tbl-reg OmcMem 0x11 offset 0x0 0x00000006 0x00000006 */
|
||||
offset = 0x11 * 4;
|
||||
@ -284,6 +270,7 @@ int get_switch_temperature(void)
|
||||
return -temperature;
|
||||
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(get_switch_temperature);
|
||||
|
||||
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;
|
||||
void __iomem *ioaddr;
|
||||
resource_size_t start;
|
||||
uint val;
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
start = iomem->start - 0x1000;
|
||||
ioaddr = devm_ioremap(&pdev->dev, start, resource_size(iomem));
|
||||
if (IS_ERR(ioaddr))
|
||||
if (IS_ERR(ioaddr)) {
|
||||
return -1;
|
||||
access = (struct ctc_access_t *) ioaddr;
|
||||
}
|
||||
access = (ctc_access_t *) ioaddr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
/*
|
||||
/* drivers/char/watchdog/ctc-wdt.c
|
||||
*
|
||||
* 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))
|
||||
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
|
||||
* division,which is 500Mhz.So we need to set the frequency division
|
||||
|
@ -1,3 +1,3 @@
|
||||
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
|
||||
|
@ -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
|
||||
*
|
||||
* Author: liuht <liuht@centecnetworks.com>
|
||||
@ -10,6 +11,7 @@
|
||||
* 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>
|
||||
@ -44,7 +46,7 @@
|
||||
|
||||
struct ctc_mdio_priv {
|
||||
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)
|
||||
@ -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)
|
||||
| CTCMAC_MDIO_CMD_OPCODE(1) | CTCMAC_MDIO_CMD_DATA(value);
|
||||
|
||||
writel(cmd, &priv->mdio_reg->mdio_soc_cmd_0[0]);
|
||||
writel(1, &priv->mdio_reg->mdio_soc_cmd_0[1]);
|
||||
writel(cmd, &priv->mdio_reg->MdioSocCmd0[0]);
|
||||
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);
|
||||
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
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)
|
||||
| CTCMAC_MDIO_CMD_OPCODE(2);
|
||||
|
||||
writel(cmd, &priv->mdio_reg->mdio_soc_cmd_0[0]);
|
||||
writel(1, &priv->mdio_reg->mdio_soc_cmd_0[1]);
|
||||
writel(cmd, &priv->mdio_reg->MdioSocCmd0[0]);
|
||||
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,
|
||||
10000);
|
||||
if (ret < 0) {
|
||||
pr_err("ctc_mdio_read1\n");
|
||||
printk(KERN_ERR "ctc_mdio_read1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
value = (readl(&priv->mdio_reg->mdio_soc_status_0) & 0xffff);
|
||||
value = (readl(&priv->mdio_reg->MdioSocStatus0) & 0xffff);
|
||||
|
||||
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;
|
||||
|
||||
writel(0x91f, &priv->mdio_reg->mdio_soc_cfg_0);
|
||||
writel(0x91f, &priv->mdio_reg->MdioSocCfg0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -146,7 +149,7 @@ static int ctc_mdio_probe(struct platform_device *pdev)
|
||||
pr_err("of iomap fail %d!\n", err);
|
||||
goto error;
|
||||
}
|
||||
priv->mdio_reg = (struct mdio_soc_regs *)priv->map;
|
||||
priv->mdio_reg = (struct MdioSoc_regs *)priv->map;
|
||||
new_bus->parent = &pdev->dev;
|
||||
platform_set_drvdata(pdev, new_bus);
|
||||
|
||||
|
2044
platform/centec-arm64/tsingma-bsp/src/ctcmac/ctcmac.c
Normal file → Executable file
2044
platform/centec-arm64/tsingma-bsp/src/ctcmac/ctcmac.c
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
*
|
||||
* Author: liuht <liuht@centecnetworks.com>
|
||||
@ -47,14 +47,18 @@
|
||||
#define CTCMAC_TOKEN_PER_PKT 10
|
||||
#define CTCMAC_TIMER_COMPENSATE 1
|
||||
|
||||
#define CTCMAC_NOR_RX1_R BIT(7)
|
||||
#define CTCMAC_NOR_RX0_R BIT(6)
|
||||
#define CTCMAC_NOR_RX1_D BIT(5)
|
||||
#define CTCMAC_NOR_RX0_D BIT(4)
|
||||
#define CTCMAC_NOR_TX_D BIT(3)
|
||||
#define CTCMAC_NOR_AN_D BIT(2)
|
||||
#define CTCMAC_NOR_LINK_DOWN BIT(1)
|
||||
#define CTCMAC_NOR_LINK_UP BIT(0)
|
||||
#define CTCMAC_NOR_RX1_R (1<<7)
|
||||
#define CTCMAC_NOR_RX0_R (1<<6)
|
||||
#define CTCMAC_NOR_RX1_D (1<<5)
|
||||
#define CTCMAC_NOR_RX0_D (1<<4)
|
||||
#define CTCMAC_NOR_TX_D (1<<3)
|
||||
#define CTCMAC_NOR_AN_D (1<<2)
|
||||
#define CTCMAC_NOR_LINK_DOWN (1<<1)
|
||||
#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
|
||||
|
||||
@ -72,7 +76,14 @@
|
||||
#define CSC_100M 0x02400000
|
||||
#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 \
|
||||
| SUPPORTED_100baseT_Full \
|
||||
@ -84,13 +95,14 @@
|
||||
struct ctcmac_skb_cb {
|
||||
unsigned int bytes_sent; /* bytes-on-wire (i.e. no FCB) */
|
||||
};
|
||||
|
||||
#define CTCMAC_CB(skb) ((struct ctcmac_skb_cb *)((skb)->cb))
|
||||
|
||||
enum ctcmac_irqinfo_id {
|
||||
CTCMAC_NORMAL = 0,
|
||||
CTCMAC_FUNC,
|
||||
CTCMAC_UNIT,
|
||||
CTCMAC_FUNC_RX0,
|
||||
CTCMAC_FUNC_RX1,
|
||||
CTCMAC_NUM_IRQS
|
||||
};
|
||||
|
||||
@ -105,6 +117,16 @@ enum ctcmac_int_type {
|
||||
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 {
|
||||
CTCMAC_AUTONEG_1000BASEX_M,
|
||||
CTCMAC_AUTONEG_PHY_M,
|
||||
@ -113,34 +135,46 @@ enum ctcmac_autoneg {
|
||||
CTCMAC_AUTONEG_MAX
|
||||
};
|
||||
|
||||
/* Per TX queue stats */
|
||||
/*
|
||||
* Per TX queue stats
|
||||
*/
|
||||
struct txq_stats {
|
||||
unsigned long tx_packets;
|
||||
unsigned long tx_bytes;
|
||||
};
|
||||
|
||||
struct tx_skb {
|
||||
struct sk_buff *skb;
|
||||
int frag_merge;
|
||||
};
|
||||
|
||||
struct ctcmac_tx_buff {
|
||||
void *vaddr;
|
||||
dma_addr_t dma;
|
||||
u32 len;
|
||||
u32 offset;
|
||||
u8 alloc;
|
||||
bool alloc;
|
||||
};
|
||||
|
||||
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];
|
||||
unsigned int num_txbdfree;
|
||||
u16 tx_ring_size;
|
||||
u16 qindex;
|
||||
u16 next_to_alloc;
|
||||
u16 next_to_clean;
|
||||
u16 skb_cur;
|
||||
u16 skb_dirty;
|
||||
u16 desc_cur;
|
||||
u16 desc_dirty;
|
||||
struct txq_stats stats;
|
||||
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 {
|
||||
unsigned long rx_packets;
|
||||
unsigned long rx_bytes;
|
||||
@ -167,6 +201,7 @@ struct ctcmac_priv_rx_q {
|
||||
u32 pps_limit;
|
||||
u32 token, token_max;
|
||||
u32 rx_trigger;
|
||||
struct napi_struct napi_rx;
|
||||
};
|
||||
|
||||
struct ctcmac_irqinfo {
|
||||
@ -189,9 +224,9 @@ struct ctcmac_private {
|
||||
struct device *dev;
|
||||
struct net_device *ndev;
|
||||
void __iomem *iobase;
|
||||
struct cpu_mac_regs __iomem *cpumac_reg;
|
||||
struct cpu_mac_mems __iomem *cpumac_mem;
|
||||
struct cpu_mac_unit_regs *cpumacu_reg;
|
||||
struct CpuMac_regs __iomem *cpumac_reg;
|
||||
struct CpuMac_mems __iomem *cpumac_mem;
|
||||
struct CpuMacUnit_regs *cpumacu_reg;
|
||||
u32 device_flags;
|
||||
int irq_num;
|
||||
int index;
|
||||
@ -213,9 +248,10 @@ struct ctcmac_private {
|
||||
|
||||
struct work_struct reset_task;
|
||||
struct platform_device *ofdev;
|
||||
struct napi_struct napi_rx;
|
||||
struct napi_struct napi_tx;
|
||||
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_tx_en;
|
||||
@ -223,8 +259,16 @@ struct ctcmac_private {
|
||||
u32 supported;
|
||||
u32 msg_enable;
|
||||
u32 int_type;
|
||||
u32 rx_int_coalesce_cnt;
|
||||
u32 tx_int_coalesce_cnt;
|
||||
u8 dfe_enable;
|
||||
u8 tx_pol_inv;
|
||||
u8 rx_pol_inv;
|
||||
struct timer_list token_timer;
|
||||
u8 version;
|
||||
u8 pause_aneg_en;
|
||||
u8 tx_pause_en;
|
||||
u8 rx_pause_en;
|
||||
};
|
||||
|
||||
struct ctcmac_pkt_stats {
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
262
platform/centec-arm64/tsingma-bsp/src/ehci-ctc/ehci-ctc.c
Normal file → Executable file
262
platform/centec-arm64/tsingma-bsp/src/ehci-ctc/ehci-ctc.c
Normal file → Executable file
@ -14,13 +14,18 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/hcd.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"
|
||||
|
||||
#define DRIVER_DESC "Centec EHCI platform driver"
|
||||
@ -28,12 +33,15 @@
|
||||
#define EHCI_MAX_RSTS 4
|
||||
#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 clk *clks[EHCI_MAX_CLKS];
|
||||
struct reset_control *rsts[EHCI_MAX_RSTS];
|
||||
struct phy **phys;
|
||||
int num_phys;
|
||||
struct reset_control *rsts;
|
||||
bool reset_on_resume;
|
||||
bool quirk_poll;
|
||||
struct timer_list poll_timer;
|
||||
struct delayed_work poll_work;
|
||||
};
|
||||
|
||||
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 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++) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
err_exit_phy:
|
||||
while (--phy_num >= 0) {
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
err_disable_clks:
|
||||
while (--clk >= 0)
|
||||
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 ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
|
||||
int clk, phy_num;
|
||||
|
||||
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
int clk;
|
||||
|
||||
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; 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 = {
|
||||
.caps_offset = 0x100,
|
||||
.dma_mask_64 = 1,
|
||||
.power_on = ehci_ctc_power_on,
|
||||
.power_suspend = ehci_ctc_power_off,
|
||||
.power_off = ehci_ctc_power_off,
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
u32 val;
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res_mem;
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ehci_ctc_priv *priv;
|
||||
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())
|
||||
return -ENODEV;
|
||||
@ -152,18 +263,17 @@ static int ehci_ctc_probe(struct platform_device *dev)
|
||||
pdata = &ehci_ctc_defaults;
|
||||
|
||||
err = dma_coerce_mask_and_coherent(&dev->dev,
|
||||
pdata->dma_mask_64 ?
|
||||
DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
|
||||
pdata->
|
||||
dma_mask_64 ? DMA_BIT_MASK(64) :
|
||||
DMA_BIT_MASK(32));
|
||||
if (err) {
|
||||
dev_err(&dev->dev, "Error: DMA mask configuration failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(dev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&dev->dev, "no irq provided");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(&ehci_ctc_hc_driver, &dev->dev,
|
||||
dev_name(&dev->dev));
|
||||
@ -174,6 +284,7 @@ static int ehci_ctc_probe(struct platform_device *dev)
|
||||
dev->dev.platform_data = pdata;
|
||||
priv = hcd_to_ehci_priv(hcd);
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
|
||||
if (pdata == &ehci_ctc_defaults && dev->dev.of_node) {
|
||||
if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
|
||||
ehci->big_endian_mmio = 1;
|
||||
@ -192,28 +303,8 @@ static int ehci_ctc_probe(struct platform_device *dev)
|
||||
"has-transaction-translator"))
|
||||
hcd->has_tt = 1;
|
||||
|
||||
priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
|
||||
"phys",
|
||||
"#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;
|
||||
}
|
||||
}
|
||||
if (soc_device_match(quirk_poll_match))
|
||||
priv->quirk_poll = true;
|
||||
|
||||
for (clk = 0; clk < EHCI_MAX_CLKS; 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;
|
||||
|
||||
priv->rsts = devm_reset_control_array_get_optional_shared(&dev->dev);
|
||||
if (IS_ERR(priv->rsts)) {
|
||||
err = PTR_ERR(priv->rsts);
|
||||
goto err_put_clks;
|
||||
}
|
||||
|
||||
err = reset_control_deassert(priv->rsts[rst]);
|
||||
err = reset_control_deassert(priv->rsts);
|
||||
if (err)
|
||||
goto err_reset;
|
||||
}
|
||||
goto err_put_clks;
|
||||
|
||||
if (pdata->big_endian_desc)
|
||||
ehci->big_endian_desc = 1;
|
||||
if (pdata->big_endian_mmio)
|
||||
@ -272,6 +359,7 @@ static int ehci_ctc_probe(struct platform_device *dev)
|
||||
if (err < 0)
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
|
||||
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_len = resource_size(res_mem);
|
||||
|
||||
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (err)
|
||||
goto err_power;
|
||||
|
||||
device_wakeup_enable(hcd->self.controller);
|
||||
device_enable_async_suspend(hcd->self.controller);
|
||||
platform_set_drvdata(dev, hcd);
|
||||
|
||||
if (priv->quirk_poll)
|
||||
quirk_poll_init(priv);
|
||||
|
||||
return err;
|
||||
|
||||
err_power:
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
err_reset:
|
||||
while (--rst >= 0)
|
||||
reset_control_assert(priv->rsts[rst]);
|
||||
reset_control_assert(priv->rsts);
|
||||
err_put_clks:
|
||||
while (--clk >= 0)
|
||||
clk_put(priv->clks[clk]);
|
||||
err_put_hcd:
|
||||
|
||||
if (pdata == &ehci_ctc_defaults)
|
||||
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_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
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);
|
||||
|
||||
if (pdata->power_off)
|
||||
pdata->power_off(dev);
|
||||
|
||||
for (rst = 0; rst < EHCI_MAX_RSTS && priv->rsts[rst]; rst++)
|
||||
reset_control_assert(priv->rsts[rst]);
|
||||
reset_control_assert(priv->rsts);
|
||||
|
||||
for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; 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_ehci_pdata *pdata = dev_get_platdata(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);
|
||||
int ret;
|
||||
|
||||
if (priv->quirk_poll)
|
||||
quirk_poll_end(priv);
|
||||
|
||||
ret = ehci_suspend(hcd, do_wakeup);
|
||||
if (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 platform_device *pdev = to_platform_device(dev);
|
||||
struct ehci_ctc_priv *priv = hcd_to_ehci_priv(hcd);
|
||||
struct device *companion_dev;
|
||||
|
||||
if (pdata->power_on) {
|
||||
int err = pdata->power_on(pdev);
|
||||
|
||||
if (err < 0)
|
||||
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);
|
||||
|
||||
if (priv->quirk_poll)
|
||||
quirk_poll_init(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2001-2002 by David Brownell
|
||||
*/
|
||||
@ -167,7 +167,6 @@ struct ehci_hcd { /* one per controller */
|
||||
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 */
|
||||
struct list_head cached_itd_list;
|
||||
struct ehci_itd *last_itd_to_free;
|
||||
@ -235,9 +234,9 @@ struct ehci_hcd { /* one per controller */
|
||||
/* irq statistics */
|
||||
#ifdef EHCI_STATS
|
||||
struct ehci_stats stats;
|
||||
# define COUNT(x) ((x)++)
|
||||
# define INCR(x) ((x)++)
|
||||
#else
|
||||
# define COUNT(x)
|
||||
# define INCR(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* debug files */
|
||||
@ -255,7 +254,7 @@ struct ehci_hcd { /* one per controller */
|
||||
struct list_head tt_list;
|
||||
|
||||
/* 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 */
|
||||
@ -263,6 +262,7 @@ static inline struct ehci_hcd *hcd_to_ehci(struct usb_hcd *hcd)
|
||||
{
|
||||
return (struct ehci_hcd *)(hcd->hcd_priv);
|
||||
}
|
||||
|
||||
static inline struct usb_hcd *ehci_to_hcd(struct ehci_hcd *ehci)
|
||||
{
|
||||
return container_of((void *)ehci, struct usb_hcd, hcd_priv);
|
||||
@ -460,7 +460,7 @@ struct ehci_iso_sched {
|
||||
struct list_head td_list;
|
||||
unsigned span;
|
||||
unsigned first_packet;
|
||||
struct ehci_iso_packet packet[0];
|
||||
struct ehci_iso_packet packet[];
|
||||
};
|
||||
|
||||
/*
|
||||
@ -739,9 +739,7 @@ static inline unsigned int ehci_readl(const struct ehci_hcd *ehci,
|
||||
__u32 __iomem * regs)
|
||||
{
|
||||
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
|
||||
return ehci_big_endian_mmio(ehci) ?
|
||||
readl_be(regs) :
|
||||
readl(regs);
|
||||
return ehci_big_endian_mmio(ehci) ? readl_be(regs) : readl(regs);
|
||||
#else
|
||||
return readl(regs);
|
||||
#endif
|
||||
@ -763,9 +761,7 @@ static inline void ehci_writel(const struct ehci_hcd *ehci,
|
||||
const unsigned int val, __u32 __iomem * regs)
|
||||
{
|
||||
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
|
||||
ehci_big_endian_mmio(ehci) ?
|
||||
writel_be(val, regs) :
|
||||
writel(val, regs);
|
||||
ehci_big_endian_mmio(ehci) ? writel_be(val, regs) : writel(val, regs);
|
||||
#else
|
||||
if (ehci->imx28_write_fix)
|
||||
imx28_ehci_writel(val, regs);
|
||||
@ -795,7 +791,8 @@ static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
|
||||
}
|
||||
#else
|
||||
static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -872,8 +869,7 @@ static inline u32 hc32_to_cpup(const struct ehci_hcd *ehci, const __hc32 *x)
|
||||
struct ehci_driver_overrides {
|
||||
size_t extra_priv_size;
|
||||
int (*reset) (struct usb_hcd * hcd);
|
||||
int (*port_power)(struct usb_hcd *hcd,
|
||||
int portnum, bool enable);
|
||||
int (*port_power) (struct usb_hcd * hcd, int portnum, bool enable);
|
||||
};
|
||||
|
||||
extern void ehci_init_driver(struct hc_driver *drv,
|
||||
|
96
platform/centec-arm64/tsingma-bsp/src/gpio-ctc/gpio-ctc.c
Normal file → Executable file
96
platform/centec-arm64/tsingma-bsp/src/gpio-ctc/gpio-ctc.c
Normal file → Executable file
@ -25,12 +25,19 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include "gpio-ctcapb.h"
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.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
|
||||
|
||||
struct ctcapb_gpio;
|
||||
|
||||
static u32 soc_v;
|
||||
|
||||
struct ctcapb_gpio_port {
|
||||
bool is_registered;
|
||||
unsigned int idx;
|
||||
@ -45,6 +52,7 @@ struct ctcapb_gpio {
|
||||
unsigned int nr_ports;
|
||||
struct GpioSoc_regs *regs;
|
||||
struct ctcapb_gpio_port *ports;
|
||||
struct regmap *regmap_base;
|
||||
};
|
||||
|
||||
static void clrsetbits(unsigned __iomem * addr, u32 clr, u32 set)
|
||||
@ -52,7 +60,7 @@ static void clrsetbits(unsigned __iomem *addr, u32 clr, u32 set)
|
||||
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);
|
||||
|
||||
@ -123,6 +131,20 @@ static void ctcapb_irq_unmask(struct irq_data *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)
|
||||
{
|
||||
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 gpio_chip *gc = &port->gc;
|
||||
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 |
|
||||
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);
|
||||
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) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
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,
|
||||
unsigned int offset, unsigned int debounce)
|
||||
unsigned offset, unsigned debounce)
|
||||
{
|
||||
struct ctcapb_gpio_port *port = gpiochip_get_data(gc);
|
||||
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,
|
||||
"interrupt-controller")) {
|
||||
"interrupt-controller"))
|
||||
{
|
||||
pp->irq = irq_of_parse_and_map(to_of_node(fwnode), 0);
|
||||
if (!pp->irq)
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned int i;
|
||||
struct resource *res;
|
||||
struct ctcapb_gpio *gpio;
|
||||
int err;
|
||||
int err, val;
|
||||
struct device *dev = &pdev->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)
|
||||
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);
|
||||
gpio->regs =
|
||||
(struct GpioSoc_regs *)devm_ioremap_resource(&pdev->dev, res);
|
||||
@ -508,8 +588,9 @@ static int ctcapb_gpio_probe(struct platform_device *pdev)
|
||||
|
||||
out_unregister:
|
||||
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]);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -520,8 +601,9 @@ static int ctcapb_gpio_remove(struct platform_device *pdev)
|
||||
struct ctcapb_gpio *gpio = platform_get_drvdata(pdev);
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Internal GPIO functions.
|
||||
*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* 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
|
||||
@ -19,9 +16,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/cdev.h>
|
||||
|
||||
enum of_gpio_flags;
|
||||
enum gpio_lookup_flags;
|
||||
struct acpi_device;
|
||||
#define GPIOCHIP_NAME "gpiochip"
|
||||
|
||||
/**
|
||||
* struct gpio_device - internal state container for GPIO devices
|
||||
@ -61,6 +56,7 @@ struct gpio_device {
|
||||
const char *label;
|
||||
void *data;
|
||||
struct list_head list;
|
||||
struct blocking_notifier_head notifier;
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
/*
|
||||
@ -73,133 +69,31 @@ struct gpio_device {
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct acpi_gpio_info - ACPI GPIO specific information
|
||||
* @adev: reference to ACPI device which consumes GPIO resource
|
||||
* @flags: GPIO initialization flags
|
||||
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo
|
||||
* @polarity: interrupt polarity as provided by ACPI
|
||||
* @triggering: triggering type as provided by ACPI
|
||||
* @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping
|
||||
*/
|
||||
struct acpi_gpio_info {
|
||||
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 */
|
||||
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 */
|
||||
struct gpio_array {
|
||||
struct gpio_desc **desc;
|
||||
unsigned int size;
|
||||
struct gpio_chip *chip;
|
||||
unsigned long *get_mask;
|
||||
unsigned long *set_mask;
|
||||
unsigned long invert_mask[];
|
||||
};
|
||||
|
||||
#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);
|
||||
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
|
||||
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
|
||||
unsigned int array_size,
|
||||
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,
|
||||
unsigned int array_size,
|
||||
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 */
|
||||
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 spinlock_t gpio_lock;
|
||||
extern struct list_head gpio_devices;
|
||||
|
||||
struct gpio_desc {
|
||||
@ -214,13 +108,26 @@ struct gpio_desc {
|
||||
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain 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_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
|
||||
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
|
||||
#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 */
|
||||
const char *label;
|
||||
/* Name of the GPIO */
|
||||
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);
|
||||
@ -238,9 +145,6 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
|
||||
return desc - &desc->gdev->descs[0];
|
||||
}
|
||||
|
||||
void devprop_gpiochip_set_names(struct gpio_chip *chip,
|
||||
const struct fwnode_handle *fwnode);
|
||||
|
||||
/* With descriptor prefix */
|
||||
|
||||
#define gpiod_emerg(desc, fmt, ...) \
|
||||
@ -264,35 +168,17 @@ void devprop_gpiochip_set_names(struct gpio_chip *chip,
|
||||
|
||||
/* With chip prefix */
|
||||
|
||||
#define chip_emerg(chip, fmt, ...) \
|
||||
dev_emerg(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
||||
#define chip_crit(chip, fmt, ...) \
|
||||
dev_crit(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
||||
#define chip_err(chip, fmt, ...) \
|
||||
dev_err(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
||||
#define chip_warn(chip, fmt, ...) \
|
||||
dev_warn(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
||||
#define chip_info(chip, fmt, ...) \
|
||||
dev_info(&chip->gpiodev->dev, "(%s): " fmt, chip->label, ##__VA_ARGS__)
|
||||
#define chip_dbg(chip, fmt, ...) \
|
||||
dev_dbg(&chip->gpiodev->dev, "(%s): " fmt, chip->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 */
|
||||
#define chip_emerg(gc, fmt, ...) \
|
||||
dev_emerg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||
#define chip_crit(gc, fmt, ...) \
|
||||
dev_crit(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||
#define chip_err(gc, fmt, ...) \
|
||||
dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||
#define chip_warn(gc, fmt, ...) \
|
||||
dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||
#define chip_info(gc, fmt, ...) \
|
||||
dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||
#define chip_dbg(gc, fmt, ...) \
|
||||
dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
|
||||
|
||||
#endif /* GPIOLIB_H */
|
||||
|
133
platform/centec-arm64/tsingma-bsp/src/i2c-ctc/i2c-ctc.c
Normal file → Executable file
133
platform/centec-arm64/tsingma-bsp/src/i2c-ctc/i2c-ctc.c
Normal file → Executable file
@ -1,4 +1,5 @@
|
||||
/* Centec I2C controller driver
|
||||
/*
|
||||
* Centec I2C controller driver
|
||||
*
|
||||
* Author: Wangyb <wangyb@centecnetworks.com>
|
||||
*
|
||||
@ -20,6 +21,10 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/export.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)
|
||||
|
||||
@ -30,7 +35,7 @@ static char *abort_sources[] = {
|
||||
[ABRT_10ADDR2_NOACK] =
|
||||
"second address byte not acknowledged (10bit mode)",
|
||||
[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_SBYTE_ACKDET] = "start byte acknowledged",
|
||||
[ABRT_SBYTE_NORSTRT] =
|
||||
@ -99,8 +104,7 @@ int i2c_ctc_init(struct ctc_i2c_dev *dev)
|
||||
__i2c_ctc_enable(dev, false);
|
||||
|
||||
/* Set SCL timing parameters */
|
||||
if ((dev->master_cfg & CTC_IC_CON_SPEED_MASK)
|
||||
== CTC_IC_CON_SPEED_FAST) {
|
||||
if ((dev->master_cfg & CTC_IC_CON_SPEED_MASK) == CTC_IC_CON_SPEED_FAST) {
|
||||
hcnt = __ctc_calc_fs_cnt(dev->clk_freq) - 14 - 4;
|
||||
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 */
|
||||
if (dev->sda_hold_time)
|
||||
if (dev->sda_hold_time) {
|
||||
ctc_writel(dev, dev->sda_hold_time, CTC_IC_SDA_HOLD);
|
||||
}
|
||||
|
||||
/* Configure Tx/Rx FIFO threshold levels */
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int timeout = 20;
|
||||
@ -146,14 +178,32 @@ static int i2c_ctc_wait_bus_not_busy(struct ctc_i2c_dev *dev)
|
||||
while (ctc_readl(dev, CTC_IC_STATUS) & CTC_IC_STATUS_ACTIVITY) {
|
||||
if (timeout <= 0) {
|
||||
dev_warn(dev->dev, "timeout waiting for bus ready\n");
|
||||
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--;
|
||||
usleep_range(1000, 1100);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
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)
|
||||
@ -199,8 +249,7 @@ static int i2c_ctc_handle_tx_abort(struct ctc_i2c_dev *dev)
|
||||
|
||||
if (abort_source & CTC_IC_TX_ABRT_NOACK) {
|
||||
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
|
||||
dev_dbg(dev->dev, "%s: %s\n", __func__,
|
||||
abort_sources[i]);
|
||||
dev_dbg(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
@ -241,7 +290,7 @@ static int i2c_ctc_interrupt_transfer(struct ctc_i2c_dev *dev)
|
||||
/* wait for tx to complete */
|
||||
if (!wait_for_completion_timeout(&dev->cmd_complete, HZ)) {
|
||||
dev_err(dev->dev, "controller timed out\n");
|
||||
i2c_ctc_init(dev);
|
||||
i2c_recover_bus(&dev->adapter);
|
||||
ret = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
@ -287,8 +336,7 @@ static int ctc_i2c_xfer_finish(struct ctc_i2c_dev *dev)
|
||||
CTC_IC_INTR_STOP_DET)) {
|
||||
ctc_readl(dev, CTC_IC_CLR_STOP_DET);
|
||||
break;
|
||||
} else if (time_after(jiffies, start_stop_det +
|
||||
I2C_STOPDET_TO)) {
|
||||
} else if (time_after(jiffies, start_stop_det + I2C_STOPDET_TO)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -401,8 +449,9 @@ static int __ctc_i2c_write(struct ctc_i2c_dev *dev, __u16 chip_addr,
|
||||
}
|
||||
data++;
|
||||
start_time_tx = jiffies;
|
||||
} else if (time_after(jiffies, start_time_tx +
|
||||
(nb * I2C_BYTE_TO))) {
|
||||
} else
|
||||
if (time_after(jiffies, start_time_tx + (nb * I2C_BYTE_TO)))
|
||||
{
|
||||
dev_err(dev->dev, "Timed out. i2c write Failed\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
@ -418,8 +467,7 @@ static int i2c_ctc_polling_transfer(struct ctc_i2c_dev *dev)
|
||||
memset(&dummy, 0, sizeof(struct i2c_msg));
|
||||
|
||||
/* 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) {
|
||||
dev_err(dev->dev, "%s: Only one or two messages are supported.",
|
||||
__func__);
|
||||
@ -469,7 +517,7 @@ static void i2c_ctc_xfer_msg(struct ctc_i2c_dev *dev)
|
||||
u8 *buf = dev->tx_buf;
|
||||
bool need_restart = false;
|
||||
|
||||
intr_mask = CTC_IC_INTR_DEFAULT_MASK;
|
||||
intr_mask = CTC_IC_INTR_MASTER_MASK;
|
||||
|
||||
/* 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)
|
||||
break;
|
||||
|
||||
/* 1 = Read */
|
||||
ctc_writel(dev, cmd | CTC_CMD_READ,
|
||||
CTC_IC_DATA_CMD);
|
||||
ctc_writel(dev, cmd | CTC_CMD_READ, CTC_IC_DATA_CMD); /* 1 = Read */
|
||||
rx_limit--;
|
||||
dev->rx_outstanding++;
|
||||
} else
|
||||
/* 0 = Write */
|
||||
ctc_writel(dev, cmd | *buf++, CTC_IC_DATA_CMD);
|
||||
ctc_writel(dev, cmd | *buf++, CTC_IC_DATA_CMD); /* 0 = Write */
|
||||
tx_limit--;
|
||||
buf_len--;
|
||||
}
|
||||
@ -538,7 +583,7 @@ static void i2c_ctc_xfer_msg(struct ctc_i2c_dev *dev)
|
||||
/* more bytes to be written */
|
||||
dev->status |= STATUS_WRITE_IN_PROGRESS;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
dev->status &= ~STATUS_WRITE_IN_PROGRESS;
|
||||
}
|
||||
|
||||
@ -584,7 +629,7 @@ static void i2c_ctc_read(struct ctc_i2c_dev *dev)
|
||||
dev->rx_buf_len = len;
|
||||
dev->rx_buf = buf;
|
||||
return;
|
||||
}
|
||||
} else
|
||||
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);
|
||||
stat = ctc_readl(dev, CTC_IC_RAW_INTR_STAT);
|
||||
|
||||
dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled,
|
||||
stat);
|
||||
if (!enabled || !(stat & ~CTC_IC_INTR_ACTIVITY))
|
||||
@ -658,7 +704,6 @@ tx_aborted:
|
||||
static u32 i2c_ctc_func(struct i2c_adapter *adap)
|
||||
{
|
||||
struct ctc_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
|
||||
return dev->functionality;
|
||||
}
|
||||
|
||||
@ -667,6 +712,10 @@ static struct i2c_algorithm i2c_ctc_algo = {
|
||||
.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)
|
||||
{
|
||||
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),
|
||||
"Centec TsingMa SoC's I2C adapter");
|
||||
adap->algo = &i2c_ctc_algo;
|
||||
adap->bus_recovery_info = &i2c_ctc_recovery_info;
|
||||
adap->dev.parent = dev->dev;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
|
||||
@ -708,6 +758,7 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
|
||||
struct resource *mem;
|
||||
int irq, ret;
|
||||
u32 clk_freq, ht;
|
||||
u32 val, i2c_num;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
@ -722,6 +773,15 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
|
||||
if (IS_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->irq = irq;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
@ -738,17 +798,6 @@ static int ctc_i2c_plat_probe(struct platform_device *pdev)
|
||||
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);
|
||||
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));
|
||||
}
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "i2c-polling-xfer"))
|
||||
dev->xfer_type = CTC_IC_POLLING_TRANSFER;
|
||||
else
|
||||
dev->xfer_type = CTC_IC_INTERRUPT_TRANSFER;
|
||||
of_property_read_u32(pdev->dev.of_node, "i2c-num", &i2c_num);
|
||||
dev->i2c_num = i2c_num;
|
||||
|
||||
dev->adapter.nr = pdev->id;
|
||||
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;
|
||||
|
||||
ret = i2c_ctc_probe(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -778,12 +826,7 @@ static int ctc_i2c_plat_remove(struct platform_device *pdev)
|
||||
|
||||
i2c_del_adapter(&dev->adapter);
|
||||
|
||||
/* 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);
|
||||
i2c_ctc_disable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Author: Wangyb <wangyb@centecnetworks.com>
|
||||
/*
|
||||
* Author: Wangyb <wangyb@centecnetworks.com>
|
||||
*
|
||||
* Copyright 2005-2018, Centec Networks (Suzhou) Co., Ltd.
|
||||
*
|
||||
@ -71,7 +72,6 @@
|
||||
#define CTC_IC_INTR_GEN_CALL 0x800
|
||||
|
||||
#define CTC_IC_INTR_DEFAULT_MASK (CTC_IC_INTR_RX_FULL | \
|
||||
CTC_IC_INTR_TX_EMPTY | \
|
||||
CTC_IC_INTR_TX_ABRT | \
|
||||
CTC_IC_INTR_STOP_DET)
|
||||
|
||||
@ -125,6 +125,38 @@
|
||||
#define CTC_IC_STATUS_MA 0x0020
|
||||
#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 {
|
||||
CTC_IC_INTERRUPT_TRANSFER,
|
||||
CTC_IC_POLLING_TRANSFER
|
||||
@ -159,4 +191,15 @@ struct ctc_i2c_dev {
|
||||
u32 clk_freq;
|
||||
u32 sda_hold_time;
|
||||
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;
|
||||
};
|
||||
|
@ -6,7 +6,7 @@
|
||||
#define SYS_TSINGMA_TEMP_TABLE_NUM 166
|
||||
#define SYS_TSINGMA_SENSOR_TIMEOUT 1000
|
||||
|
||||
struct ctc_switch_cmd_status_t {
|
||||
typedef struct ctc_switch_cmd_status_s {
|
||||
u32 cmdReadType:1;
|
||||
u32 pcieReqCmdChk:3;
|
||||
u32 cmdEntryWords:4;
|
||||
@ -24,18 +24,18 @@ struct ctc_switch_cmd_status_t {
|
||||
u32 pciePoisoned:1;
|
||||
u32 regProcState:3;
|
||||
u32 pcieReqOverlap:1;
|
||||
};
|
||||
} ctc_switch_cmd_status_t;
|
||||
|
||||
union ctc_switch_cmd_status_u_t {
|
||||
struct ctc_switch_cmd_status_t cmd_status;
|
||||
typedef union drv_pci_cmd_status_u_e {
|
||||
ctc_switch_cmd_status_t cmd_status;
|
||||
u32 val;
|
||||
};
|
||||
} ctc_switch_cmd_status_u_t;
|
||||
|
||||
struct ctc_access_t {
|
||||
typedef struct ctc_access_s {
|
||||
u32 cmd_status;
|
||||
u32 addr;
|
||||
u32 data[16];
|
||||
};
|
||||
} ctc_access_t;
|
||||
|
||||
extern int ctc5236_switch_read(u32 offset, u32 len, u32 * p_value);
|
||||
extern int ctc5236_switch_write(u32 offset, u32 len, u32 * p_value);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Core private header for the pin control subsystem
|
||||
*
|
||||
@ -5,8 +6,6 @@
|
||||
* Written on behalf of Linaro for ST-Ericsson
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#include <linux/kref.h>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
extern struct pinctrl_gpio_range *
|
||||
pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin);
|
||||
|
||||
int pinctrl_register_map(const struct pinctrl_map *maps, unsigned num_maps,
|
||||
bool dup);
|
||||
void pinctrl_unregister_map(const struct pinctrl_map *map);
|
||||
extern struct pinctrl_gpio_range *pinctrl_find_gpio_range_from_pin_nolock(struct
|
||||
pinctrl_dev
|
||||
*pctldev,
|
||||
unsigned
|
||||
int
|
||||
pin);
|
||||
|
||||
extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
|
||||
extern int pinctrl_force_default(struct pinctrl_dev *pctldev);
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Internal interface between the core pin control system and the
|
||||
* pin config portions
|
||||
@ -7,8 +8,6 @@
|
||||
* Based on bits of regulator core, gpio core and clk core
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
* License terms: GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PINCONF
|
||||
|
44
platform/centec-arm64/tsingma-bsp/src/pinctrl-ctc/pinctrl-ctc.c
Normal file → Executable file
44
platform/centec-arm64/tsingma-bsp/src/pinctrl-ctc/pinctrl-ctc.c
Normal file → Executable file
@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/bitops.h>
|
||||
@ -24,6 +23,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include "../include/sysctl.h"
|
||||
#include "pinctrl-ctc.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,
|
||||
unsigned int num)
|
||||
unsigned num)
|
||||
{
|
||||
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 ERR_PTR(-EINVAL);
|
||||
}
|
||||
@ -195,8 +196,10 @@ static int ctc_pinctrl_parse_dt(struct platform_device *pdev,
|
||||
struct device_node *child;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "ctc,pinctrl-bank0", &bank0_pins);
|
||||
if (ret < 0) {
|
||||
@ -229,13 +232,17 @@ static int ctc_pinctrl_parse_dt(struct platform_device *pdev,
|
||||
|
||||
info->functions = devm_kzalloc(dev, info->nfunctions *
|
||||
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;
|
||||
}
|
||||
|
||||
info->groups = devm_kzalloc(dev, info->ngroups *
|
||||
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;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
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,
|
||||
unsigned int selector)
|
||||
unsigned selector)
|
||||
{
|
||||
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,
|
||||
unsigned int selector, const unsigned int **pins,
|
||||
unsigned int *npins)
|
||||
unsigned selector, const unsigned **pins,
|
||||
unsigned *npins)
|
||||
{
|
||||
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,
|
||||
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);
|
||||
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,
|
||||
unsigned int selector)
|
||||
unsigned selector)
|
||||
{
|
||||
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,
|
||||
unsigned int selector, const char *const **groups,
|
||||
unsigned int *const num_groups)
|
||||
unsigned selector, const char *const **groups,
|
||||
unsigned *const num_groups)
|
||||
{
|
||||
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,
|
||||
unsigned int pin)
|
||||
unsigned pin)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned int selector,
|
||||
unsigned int group)
|
||||
static int ctc_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
|
||||
unsigned group)
|
||||
{
|
||||
struct ctc_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
|
||||
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",
|
||||
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.
|
||||
*/
|
||||
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,
|
||||
struct pinctrl_map *map, unsigned int num_maps)
|
||||
struct pinctrl_map *map, unsigned num_maps)
|
||||
{
|
||||
}
|
||||
|
||||
|
41
platform/centec-arm64/tsingma-bsp/src/pwm-ctc/pwm-ctc.c
Normal file → Executable file
41
platform/centec-arm64/tsingma-bsp/src/pwm-ctc/pwm-ctc.c
Normal file → Executable file
@ -1,4 +1,5 @@
|
||||
/* Centec PWM driver
|
||||
/*
|
||||
* Centec PWM driver
|
||||
*
|
||||
* Author: wangyb <wangyb@centecnetworks.com>
|
||||
*
|
||||
@ -60,7 +61,6 @@ static inline u32 ctc_pwm_readl(struct ctc_pwm_chip *chip, unsigned int num,
|
||||
unsigned long offset)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
regmap_read(chip->regmap_base,
|
||||
offsetof(struct SysCtl_regs,
|
||||
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)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
regmap_read(chip->regmap_base,
|
||||
offsetof(struct SysCtl_regs,
|
||||
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);
|
||||
u32 cur_value;
|
||||
u32 duty_cycle = 0;
|
||||
u32 period_cycle = 0;
|
||||
|
||||
duty_ns = duty_ns / 1000;
|
||||
period_ns = period_ns / 1000;
|
||||
duty_cycle = duty_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_ns = duty_ns & CTC_MAX_DUTY_PWM;
|
||||
ctc_pwm_writel(pc, pwm->hwpwm, CTC_DUTY_PWM, duty_ns);
|
||||
duty_cycle = duty_cycle & CTC_MAX_DUTY_PWM;
|
||||
ctc_pwm_writel(pc, pwm->hwpwm, CTC_DUTY_PWM, duty_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_MAX_PERIOD_PWM);
|
||||
cur_value |= period_ns << 0;
|
||||
cur_value |= period_cycle << 0;
|
||||
ctc_pwm_writel(pc, pwm->hwpwm, CTC_CR_PWM, cur_value);
|
||||
|
||||
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->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);
|
||||
|
||||
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,
|
||||
@ -154,10 +164,10 @@ static int ctc_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
u32 duty_tach;
|
||||
|
||||
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;
|
||||
result->duty_cycle = duty_tach * 1000;
|
||||
result->duty_cycle = duty_tach * 1000; // in nanoseconds
|
||||
|
||||
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,
|
||||
"ctc,sysctrl");
|
||||
pctldev = devm_kzalloc(&pdev->dev, sizeof(*pctldev), GFP_KERNEL);
|
||||
if (!pctldev)
|
||||
if (!pctldev) {
|
||||
return -1;
|
||||
}
|
||||
pctldev->p = pinctrl_get(&pdev->dev);
|
||||
state = pinctrl_lookup_state(pctldev->p, PINCTRL_STATE_DEFAULT);
|
||||
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++) {
|
||||
cur_value = ctc_pwm_readl(pc, i, CTC_CR_PWM);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* rtc class driver for the SD2405 chip
|
||||
/*
|
||||
* rtc class driver for the SD2405 chip
|
||||
*
|
||||
* Author: Dale Farnsworth <dale@farnsworth.org>
|
||||
*
|
||||
@ -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)
|
||||
{
|
||||
int rc;
|
||||
|
||||
u8 temp_reg[SD2405_REG_LEN + 1] = { 0 };
|
||||
memcpy(&temp_reg[1], buf, SD2405_REG_LEN);
|
||||
|
||||
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}
|
||||
};
|
||||
|
||||
memcpy(&temp_reg[1], buf, SD2405_REG_LEN);
|
||||
|
||||
rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (rc != ARRAY_SIZE(msgs))
|
||||
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)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = i2c_smbus_write_byte_data(client, SD2405_REG_CTRL1, 0);
|
||||
rc += i2c_smbus_write_byte_data(client, SD2405_REG_CTRL2, 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)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = i2c_smbus_write_byte_data(client, SD2405_REG_CTRL2,
|
||||
SD2405_REG_CONTROL1_WRITE);
|
||||
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)
|
||||
{
|
||||
#if 0
|
||||
struct rtc_device *rtc = i2c_get_clientdata(client);
|
||||
|
||||
if (rtc)
|
||||
rtc_device_unregister(rtc);
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -199,17 +199,20 @@ static const struct rtc_class_ops sd2405_rtc_ops = {
|
||||
static int
|
||||
sd2405_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct rtc_device *rtc;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
|
||||
|
||||
rtc = rtc_device_register(sd2405_driver.driver.name,
|
||||
&client->dev, &sd2405_rtc_ops, THIS_MODULE);
|
||||
rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_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);
|
||||
|
||||
|
185
platform/centec-arm64/tsingma-bsp/src/sdhci-ctc5236/sdhci-ctc5236.c
Normal file → Executable file
185
platform/centec-arm64/tsingma-bsp/src/sdhci-ctc5236/sdhci-ctc5236.c
Normal file → Executable 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>
|
||||
*
|
||||
* 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.
|
||||
* Copyright 2005-2020, Centec Networks (Suzhou) Co., Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
@ -27,6 +19,8 @@
|
||||
#include <asm-generic/delay.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/raid/pq.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#define REG_OFFSET_ADDR 0x500
|
||||
#define MSHC_CTRL_R 0x8
|
||||
@ -37,28 +31,31 @@
|
||||
#define MAX_TUNING_LOOP 0x80
|
||||
#define MIN_TUNING_LOOP 0x0
|
||||
#define TUNE_CTRL_STEP 1
|
||||
#define EMMC_CTRL_R 0x2c
|
||||
|
||||
struct regmap *regmap_base;
|
||||
#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)
|
||||
{
|
||||
if (unlikely(reg == SDHCI_HOST_VERSION))
|
||||
if (unlikely(reg == SDHCI_HOST_VERSION)) {
|
||||
return SDHCI_SPEC_300;
|
||||
}
|
||||
|
||||
return readw(host->ioaddr + reg);
|
||||
}
|
||||
|
||||
static u32 sdhci_ctc5236_readl(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 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);
|
||||
u32 ret;
|
||||
ret = readl(host->ioaddr + reg);
|
||||
|
||||
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)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (clock == SDHCI_REFCLK_150M) {
|
||||
/* SDHCI reference clock change 150M */
|
||||
regmap_read(regmap_base,
|
||||
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 &
|
||||
(~SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK);
|
||||
val |=
|
||||
((0x8 & SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK)) << 0;
|
||||
((0x8 &
|
||||
SYS_CLK_PERI_CFG_W0_CFG_DIV_MSH_REF_CNT_MASK)) <<
|
||||
0;
|
||||
regmap_write(regmap_base,
|
||||
offsetof(struct SysCtl_regs, SysClkPeriCfg), val);
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
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);
|
||||
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 */
|
||||
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) {
|
||||
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))
|
||||
break;
|
||||
|
||||
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
|
||||
min += TUNE_CTRL_STEP;
|
||||
sdhci_writel(host, min, REG_OFFSET_ADDR + AT_STAT_R);
|
||||
}
|
||||
|
||||
/* find the maxinum delay which can not pass tuning */
|
||||
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) {
|
||||
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)) {
|
||||
max -= TUNE_CTRL_STEP;
|
||||
break;
|
||||
}
|
||||
|
||||
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
|
||||
max += TUNE_CTRL_STEP;
|
||||
sdhci_writel(host, max, REG_OFFSET_ADDR + AT_STAT_R);
|
||||
}
|
||||
|
||||
/* use average delay to get the best timing */
|
||||
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);
|
||||
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",
|
||||
ret ? "failed" : "passed", avg, ret);
|
||||
dev_info(mmc_dev(host->mmc),
|
||||
"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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
.read_w = sdhci_ctc5236_readw,
|
||||
.read_l = sdhci_ctc5236_readl,
|
||||
@ -234,12 +284,14 @@ static const struct sdhci_ops sdhci_ctc5236_ops = {
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.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 = {
|
||||
.ops = &sdhci_ctc5236_ops,
|
||||
.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)
|
||||
@ -247,12 +299,40 @@ static int sdhci_ctc5236_probe(struct platform_device *pdev)
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
int ret, val;
|
||||
u32 extra;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_ctc5236_pdata, 0);
|
||||
if (IS_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");
|
||||
if (IS_ERR(clk)) {
|
||||
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;
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
regmap_base =
|
||||
syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "ctc,sysctrl");
|
||||
if (IS_ERR(regmap_base))
|
||||
return PTR_ERR(regmap_base);
|
||||
if (version == CTC_REV_TM_1_0) {
|
||||
if (host->mmc->caps & MMC_CAP_1_8V_DDR) {
|
||||
host->mmc->caps &= ~MMC_CAP_1_8V_DDR;
|
||||
printk("%s, not support DDR Mode\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask);
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err_sdhci_add;
|
||||
if (host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR) {
|
||||
host->mmc->caps2 &= ~MMC_CAP2_HS200_1_8V_SDR;
|
||||
printk("%s, not support Hs200 Mode\n", __func__);
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
@ -303,5 +384,5 @@ static struct platform_driver sdhci_ctc5236_driver = {
|
||||
module_platform_driver(sdhci_ctc5236_driver);
|
||||
|
||||
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");
|
||||
|
@ -1,11 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2010 MontaVista Software, LLC.
|
||||
*
|
||||
* 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
|
||||
@ -28,7 +25,7 @@ struct sdhci_pltfm_host {
|
||||
unsigned int clock;
|
||||
u16 xfer_mode_shadow;
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
unsigned long private[] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
|
||||
@ -90,7 +87,12 @@ static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
|
||||
}
|
||||
#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,
|
||||
const struct sdhci_pltfm_data *pdata,
|
||||
@ -109,8 +111,20 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
|
||||
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_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 */
|
||||
|
@ -1,18 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
|
||||
*
|
||||
* Header file for Host Controller registers and I/O accessors.
|
||||
*
|
||||
* 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
|
||||
#define __SDHCI_HW_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
@ -28,6 +25,7 @@
|
||||
|
||||
#define SDHCI_DMA_ADDRESS 0x00
|
||||
#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
|
||||
#define SDHCI_32BIT_BLK_CNT SDHCI_DMA_ADDRESS
|
||||
|
||||
#define SDHCI_BLOCK_SIZE 0x04
|
||||
#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_AUTO_CMD12 0x04
|
||||
#define SDHCI_TRNS_AUTO_CMD23 0x08
|
||||
#define SDHCI_TRNS_AUTO_SEL 0x0C
|
||||
#define SDHCI_TRNS_READ 0x10
|
||||
#define SDHCI_TRNS_MULTI 0x20
|
||||
|
||||
@ -71,6 +70,10 @@
|
||||
#define SDHCI_SPACE_AVAILABLE 0x00000400
|
||||
#define SDHCI_DATA_AVAILABLE 0x00000800
|
||||
#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_DATA_LVL_MASK 0x00F00000
|
||||
#define SDHCI_DATA_LVL_SHIFT 20
|
||||
@ -86,6 +89,7 @@
|
||||
#define SDHCI_CTRL_ADMA1 0x08
|
||||
#define SDHCI_CTRL_ADMA32 0x10
|
||||
#define SDHCI_CTRL_ADMA64 0x18
|
||||
#define SDHCI_CTRL_ADMA3 0x18
|
||||
#define SDHCI_CTRL_8BITBUS 0x20
|
||||
#define SDHCI_CTRL_CDTEST_INS 0x40
|
||||
#define SDHCI_CTRL_CDTEST_EN 0x80
|
||||
@ -111,6 +115,7 @@
|
||||
#define SDHCI_DIV_HI_MASK 0x300
|
||||
#define SDHCI_PROG_CLOCK_MODE 0x0020
|
||||
#define SDHCI_CLOCK_CARD_EN 0x0004
|
||||
#define SDHCI_CLOCK_PLL_EN 0x0008
|
||||
#define SDHCI_CLOCK_INT_STABLE 0x0002
|
||||
#define SDHCI_CLOCK_INT_EN 0x0001
|
||||
|
||||
@ -189,15 +194,16 @@
|
||||
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
|
||||
#define SDHCI_CTRL_EXEC_TUNING 0x0040
|
||||
#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_CAPABILITIES 0x40
|
||||
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
|
||||
#define SDHCI_TIMEOUT_CLK_SHIFT 0
|
||||
#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0)
|
||||
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
|
||||
#define SDHCI_CLOCK_BASE_MASK 0x00003F00
|
||||
#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00
|
||||
#define SDHCI_CLOCK_BASE_SHIFT 8
|
||||
#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8)
|
||||
#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8)
|
||||
#define SDHCI_MAX_BLOCK_MASK 0x00030000
|
||||
#define SDHCI_MAX_BLOCK_SHIFT 16
|
||||
#define SDHCI_CAN_DO_8BIT 0x00040000
|
||||
@ -209,33 +215,28 @@
|
||||
#define SDHCI_CAN_VDD_330 0x01000000
|
||||
#define SDHCI_CAN_VDD_300 0x02000000
|
||||
#define SDHCI_CAN_VDD_180 0x04000000
|
||||
#define SDHCI_CAN_64BIT_V4 0x08000000
|
||||
#define SDHCI_CAN_64BIT 0x10000000
|
||||
|
||||
#define SDHCI_CAPABILITIES_1 0x44
|
||||
#define SDHCI_SUPPORT_SDR50 0x00000001
|
||||
#define SDHCI_SUPPORT_SDR104 0x00000002
|
||||
#define SDHCI_SUPPORT_DDR50 0x00000004
|
||||
#define SDHCI_DRIVER_TYPE_A 0x00000010
|
||||
#define SDHCI_DRIVER_TYPE_C 0x00000020
|
||||
#define SDHCI_DRIVER_TYPE_D 0x00000040
|
||||
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
|
||||
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
|
||||
#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8)
|
||||
#define SDHCI_USE_SDR50_TUNING 0x00002000
|
||||
#define SDHCI_RETUNING_MODE_MASK 0x0000C000
|
||||
#define SDHCI_RETUNING_MODE_SHIFT 14
|
||||
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
|
||||
#define SDHCI_CLOCK_MUL_SHIFT 16
|
||||
#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
|
||||
#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
|
||||
#define SDHCI_CAN_DO_ADMA3 0x08000000
|
||||
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
|
||||
|
||||
#define SDHCI_CAPABILITIES_1 0x44
|
||||
|
||||
#define SDHCI_MAX_CURRENT 0x48
|
||||
#define SDHCI_MAX_CURRENT_LIMIT 0xFF
|
||||
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
|
||||
#define SDHCI_MAX_CURRENT_330_SHIFT 0
|
||||
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
|
||||
#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_LIMIT GENMASK(7, 0)
|
||||
#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
|
||||
#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
|
||||
#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
|
||||
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
|
||||
|
||||
/* 4C-4F reserved for more max current */
|
||||
@ -252,18 +253,16 @@
|
||||
|
||||
/* 60-FB reserved */
|
||||
|
||||
#define SDHCI_PRESET_FOR_HIGH_SPEED 0x64
|
||||
#define SDHCI_PRESET_FOR_SDR12 0x66
|
||||
#define SDHCI_PRESET_FOR_SDR25 0x68
|
||||
#define SDHCI_PRESET_FOR_SDR50 0x6A
|
||||
#define SDHCI_PRESET_FOR_SDR104 0x6C
|
||||
#define SDHCI_PRESET_FOR_DDR50 0x6E
|
||||
#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
|
||||
#define SDHCI_PRESET_DRV_MASK 0xC000
|
||||
#define SDHCI_PRESET_DRV_SHIFT 14
|
||||
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
|
||||
#define SDHCI_PRESET_CLKGEN_SEL_SHIFT 10
|
||||
#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF
|
||||
#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0
|
||||
#define SDHCI_PRESET_DRV_MASK GENMASK(15, 14)
|
||||
#define SDHCI_PRESET_CLKGEN_SEL BIT(10)
|
||||
#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
|
||||
|
||||
#define SDHCI_SLOT_INT_STATUS 0xFC
|
||||
|
||||
@ -275,6 +274,9 @@
|
||||
#define SDHCI_SPEC_100 0
|
||||
#define SDHCI_SPEC_200 1
|
||||
#define SDHCI_SPEC_300 2
|
||||
#define SDHCI_SPEC_400 3
|
||||
#define SDHCI_SPEC_410 4
|
||||
#define SDHCI_SPEC_420 5
|
||||
|
||||
/*
|
||||
* End of controller registers.
|
||||
@ -310,8 +312,14 @@ struct sdhci_adma2_32_desc {
|
||||
*/
|
||||
#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
|
||||
@ -391,8 +399,12 @@ struct sdhci_host {
|
||||
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
|
||||
/* Controller reports inverted write-protect state */
|
||||
#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 */
|
||||
#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 */
|
||||
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
|
||||
/* Controller cannot do multi-block transfers */
|
||||
@ -455,9 +467,17 @@ struct sdhci_host {
|
||||
* obtainable timeout.
|
||||
*/
|
||||
#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 */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
phys_addr_t mapbase; /* physical address base */
|
||||
char *bounce_buffer; /* For packing SDMA reads/writes */
|
||||
dma_addr_t bounce_addr;
|
||||
unsigned int bounce_buffer_size;
|
||||
@ -485,7 +505,6 @@ struct sdhci_host {
|
||||
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
|
||||
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
||||
#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_HS400_TUNING (1<<13) /* Tuning for HS400 */
|
||||
#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 pending_reset; /* Cmd/data reset is pending */
|
||||
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_command *cmd; /* Current command */
|
||||
struct mmc_command *data_cmd; /* Current data command */
|
||||
struct mmc_command *deferred_cmd; /* Deferred command */
|
||||
struct mmc_data *data; /* Current data request */
|
||||
unsigned int data_early:1; /* Data finished before cmd */
|
||||
|
||||
@ -527,17 +550,25 @@ struct sdhci_host {
|
||||
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
||||
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 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 caps1; /* CAPABILITY_1 */
|
||||
bool read_caps; /* Capability flags have been read */
|
||||
|
||||
bool sdhci_core_to_disable_vqmmc; /* sdhci core can disable vqmmc */
|
||||
unsigned int ocr_avail_sdio; /* OCR bit masks */
|
||||
unsigned int ocr_avail_sd;
|
||||
unsigned int ocr_avail_mmc;
|
||||
@ -559,18 +590,23 @@ struct sdhci_host {
|
||||
|
||||
unsigned int tuning_count; /* Timer count for re-tuning */
|
||||
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_2 1
|
||||
#define SDHCI_TUNING_MODE_3 2
|
||||
/* Delay (ms) between tuning commands */
|
||||
int tuning_delay;
|
||||
int tuning_loop_count;
|
||||
|
||||
/* Host SDMA buffer boundary. */
|
||||
u32 sdma_boundary;
|
||||
|
||||
/* Host ADMA table count */
|
||||
u32 adma_table_cnt;
|
||||
|
||||
u64 data_timeout;
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
unsigned long private[] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct sdhci_ops {
|
||||
@ -589,6 +625,7 @@ struct sdhci_ops {
|
||||
|
||||
u32(*irq) (struct sdhci_host * host, u32 intmask);
|
||||
|
||||
int (*set_dma_mask) (struct sdhci_host * host);
|
||||
int (*enable_dma) (struct sdhci_host * host);
|
||||
unsigned int (*get_max_clock) (struct sdhci_host * host);
|
||||
unsigned int (*get_min_clock) (struct sdhci_host * host);
|
||||
@ -608,6 +645,14 @@ struct sdhci_ops {
|
||||
void (*adma_workaround) (struct sdhci_host * host, u32 intmask);
|
||||
void (*card_event) (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
|
||||
@ -703,33 +748,31 @@ static inline void *sdhci_priv(struct sdhci_host *host)
|
||||
}
|
||||
|
||||
void sdhci_card_detect(struct sdhci_host *host);
|
||||
void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps,
|
||||
u32 *caps1);
|
||||
void __sdhci_read_caps(struct sdhci_host *host, const u16 * ver,
|
||||
const u32 * caps, const u32 * caps1);
|
||||
int sdhci_setup_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);
|
||||
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)
|
||||
{
|
||||
__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,
|
||||
unsigned int *actual_clock);
|
||||
void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
|
||||
void sdhci_enable_clk(struct sdhci_host *host, u16 clk);
|
||||
void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
|
||||
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,
|
||||
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_reset(struct sdhci_host *host, u8 mask);
|
||||
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,
|
||||
struct mmc_ios *ios);
|
||||
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
|
||||
int sdhci_suspend_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_resume_host(struct sdhci_host *host);
|
||||
int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset);
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
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_end_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_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 */
|
||||
|
@ -195,8 +195,9 @@ static void ctc_fill_tx_buf(struct ctc_qspi *ctc_qspi, u8 *offset, u8 *buf,
|
||||
u8 off = *offset;
|
||||
|
||||
while (i < len) {
|
||||
if (check_buf_ok(buf, i))
|
||||
if (check_buf_ok(buf, i)) {
|
||||
fill_tx_entry(ctc_qspi, buf, i, off);
|
||||
}
|
||||
|
||||
if (off % 4 == 0) {
|
||||
ctc_write_tx_buf(ctc_qspi, off, ctc_qspi->tx_entry);
|
||||
@ -218,9 +219,10 @@ static void ctc_fill_pp_buf(struct ctc_qspi *ctc_qspi, u32 *offset, u8 *buf,
|
||||
|
||||
while (i < len) {
|
||||
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_write_tx_buf(ctc_qspi, off, ctc_qspi->tx_entry);
|
||||
ctc_qspi->tx_entry = 0;
|
||||
|
||||
@ -303,15 +305,17 @@ static void ctc_qspi_pio_ctrl(struct ctc_qspi *ctc_qspi)
|
||||
{
|
||||
u32 ctrl = 0;
|
||||
|
||||
ctrl = CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) |
|
||||
CTRL_PRE_CYCLE(ctc_qspi->precycle) |
|
||||
CTRL_POST_CYCLE(ctc_qspi->postcycle) |
|
||||
CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef) |
|
||||
CTRL_SOUT3_DEFAULT(ctc_qspi->sout3def) |
|
||||
CTRL_SOUT2_DEFAULT(ctc_qspi->sout2def) |
|
||||
CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) |
|
||||
CTRL_CS(ctc_qspi->cs_select) |
|
||||
CTRL_DIV_SCLK(ctc_qspi->clkdiv);
|
||||
ctrl =
|
||||
CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) | CTRL_PRE_CYCLE(ctc_qspi->
|
||||
precycle)
|
||||
| CTRL_POST_CYCLE(ctc_qspi->
|
||||
postcycle) | CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef)
|
||||
| CTRL_SOUT3_DEFAULT(ctc_qspi->
|
||||
sout3def) | CTRL_SOUT2_DEFAULT(ctc_qspi->
|
||||
sout2def)
|
||||
| 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,
|
||||
0xffffffff);
|
||||
@ -321,15 +325,17 @@ static void ctc_qspi_pp_ctrl(struct ctc_qspi *ctc_qspi)
|
||||
{
|
||||
u32 ctrl = 0;
|
||||
|
||||
ctrl = CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) |
|
||||
CTRL_PRE_CYCLE(ctc_qspi->precycle) |
|
||||
CTRL_POST_CYCLE(ctc_qspi->postcycle) |
|
||||
CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef) |
|
||||
CTRL_SOUT3_DEFAULT(ctc_qspi->sout3def) |
|
||||
CTRL_SOUT2_DEFAULT(ctc_qspi->sout2def) |
|
||||
CTRL_SOUT1_DEFAULT(ctc_qspi->sout1def) |
|
||||
CTRL_CS(ctc_qspi->cs_select) |
|
||||
CTRL_DIV_SCLK(ctc_qspi->clkdiv);
|
||||
ctrl =
|
||||
CTRL_IDLE_CYCLE(ctc_qspi->idlecycle) | CTRL_PRE_CYCLE(ctc_qspi->
|
||||
precycle)
|
||||
| CTRL_POST_CYCLE(ctc_qspi->
|
||||
postcycle) | CTRL_SCLK_DEFAULT(ctc_qspi->sclkdef)
|
||||
| CTRL_SOUT3_DEFAULT(ctc_qspi->
|
||||
sout3def) | CTRL_SOUT2_DEFAULT(ctc_qspi->
|
||||
sout2def)
|
||||
| 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);
|
||||
}
|
||||
@ -354,7 +360,6 @@ static void ctc_extra_rx_buf(struct ctc_qspi *ctc_qspi, u8 offset, u8 *buf,
|
||||
u8 len)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (i < len) {
|
||||
ctc_read_rx_buf(ctc_qspi, offset, &buf[i++]);
|
||||
offset--;
|
||||
@ -523,12 +528,14 @@ 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 )
|
||||
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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -558,7 +565,7 @@ static int ctc_qspi_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
master->setup = ctc_qspi_setup;
|
||||
master->transfer_one_message = ctc_qspi_start_transfer_one;
|
||||
|
@ -1176,7 +1176,12 @@ dal_alloc_dma_pool(int lchip, int size)
|
||||
if (use_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
|
||||
{
|
||||
@ -1205,7 +1210,12 @@ dal_alloc_dma_pool(int lchip, int size)
|
||||
/* Get DMA memory from kernel */
|
||||
dma_virt_base[lchip] = _dal_pgalloc(size);
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -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->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));
|
||||
|
||||
/*0: little endian 1: big endian*/
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/pca954x.h>
|
||||
#include "../../pca954x/ctc-pca954x.h"
|
||||
#include <linux/device.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/leds.h>
|
||||
@ -95,7 +95,7 @@ static struct pca954x_platform_data i2c_dev_pca9548_platform_data = {
|
||||
.num_modes = PCA9548_CHANNEL_NUM,
|
||||
};
|
||||
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,
|
||||
};
|
||||
static struct i2c_client *i2c_client_pca9548x = NULL;
|
||||
@ -110,7 +110,7 @@ static int e582_48x2q4z_init_i2c_pca9548(void)
|
||||
}
|
||||
|
||||
/* 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))
|
||||
{
|
||||
i2c_client_pca9548x = NULL;
|
||||
@ -150,7 +150,7 @@ static int e582_48x2q4z_init_i2c_adt7470(void)
|
||||
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)){
|
||||
i2c_client_adt7470 = NULL;
|
||||
printk(KERN_CRIT "install e582_48x2q4z board adt7470 failed\n");
|
||||
@ -206,14 +206,14 @@ static int e582_48x2q4z_init_i2c_psu(void)
|
||||
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)){
|
||||
i2c_client_psu1 = NULL;
|
||||
printk(KERN_CRIT "create e582_48x2q4z board i2c client psu1 failed\n");
|
||||
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)){
|
||||
i2c_client_psu2 = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
i2c_client_epld = NULL;
|
||||
@ -362,7 +362,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio0 = NULL;
|
||||
@ -370,7 +370,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio1 = NULL;
|
||||
@ -378,7 +378,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio2 = NULL;
|
||||
@ -386,7 +386,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio3 = NULL;
|
||||
@ -394,7 +394,7 @@ static int e582_48x2q4z_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio4 = NULL;
|
||||
|
@ -25,12 +25,13 @@ if [ "$1" == "init" ]; then
|
||||
modprobe i2c-dev
|
||||
modprobe i2c-mux
|
||||
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
|
||||
modprobe lm77
|
||||
modprobe tun
|
||||
modprobe dal
|
||||
modprobe centec_at24c64
|
||||
modprobe at24
|
||||
echo 24c64 0x57 > /sys/bus/i2c/devices/i2c-0/new_device
|
||||
modprobe centec_e582_48x2q4z_platform
|
||||
i2cset -y 15 0x21 0x18 0x0
|
||||
i2cset -y 15 0x21 0x19 0x0
|
||||
@ -69,9 +70,9 @@ elif [ "$1" == "deinit" ]; then
|
||||
kill -9 $(pidof platform_monitor) > /dev/null 2>&1
|
||||
rm -rf /usr/bin/platform_monitor
|
||||
modprobe -r centec_e582_48x2q4z_platform
|
||||
modprobe -r centec_at24c64
|
||||
modprobe -r at24
|
||||
modprobe -r dal
|
||||
modprobe -r i2c-mux-pca954x
|
||||
modprobe -r ctc-i2c-mux-pca954x
|
||||
modprobe -r i2c-dev
|
||||
else
|
||||
echo "e582-48x2q4z_platform : Invalid option !"
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/platform_data/pca954x.h>
|
||||
#include "../../pca954x/ctc-pca954x.h"
|
||||
#include <linux/device.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/leds.h>
|
||||
@ -95,7 +95,7 @@ static struct pca954x_platform_data i2c_dev_pca9548_platform_data = {
|
||||
.num_modes = PCA9548_CHANNEL_NUM,
|
||||
};
|
||||
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,
|
||||
};
|
||||
static struct i2c_client *i2c_client_pca9548x = NULL;
|
||||
@ -110,7 +110,7 @@ static int e582_48x6q_init_i2c_pca9548(void)
|
||||
}
|
||||
|
||||
/* 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))
|
||||
{
|
||||
i2c_client_pca9548x = NULL;
|
||||
@ -150,7 +150,7 @@ static int e582_48x6q_init_i2c_adt7470(void)
|
||||
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)){
|
||||
i2c_client_adt7470 = NULL;
|
||||
printk(KERN_CRIT "install e582_48x6q board adt7470 failed\n");
|
||||
@ -206,14 +206,14 @@ static int e582_48x6q_init_i2c_psu(void)
|
||||
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)){
|
||||
i2c_client_psu1 = NULL;
|
||||
printk(KERN_CRIT "create e582_48x6q board i2c client psu1 failed\n");
|
||||
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)){
|
||||
i2c_client_psu2 = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
i2c_client_epld = NULL;
|
||||
@ -317,7 +317,7 @@ static int e582_48x6q_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio0 = NULL;
|
||||
@ -325,7 +325,7 @@ static int e582_48x6q_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio1 = NULL;
|
||||
@ -333,7 +333,7 @@ static int e582_48x6q_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio2 = NULL;
|
||||
@ -341,7 +341,7 @@ static int e582_48x6q_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio3 = NULL;
|
||||
@ -349,7 +349,7 @@ static int e582_48x6q_init_i2c_gpio(void)
|
||||
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))
|
||||
{
|
||||
i2c_client_gpio4 = NULL;
|
||||
|
@ -25,7 +25,7 @@ if [ "$1" == "init" ]; then
|
||||
modprobe i2c-dev
|
||||
modprobe i2c-mux
|
||||
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 0x20 0x1b 0x0
|
||||
i2cset -y 0 0x20 0xb 0x0
|
||||
@ -42,7 +42,8 @@ if [ "$1" == "init" ]; then
|
||||
modprobe lm77
|
||||
modprobe tun
|
||||
modprobe dal
|
||||
modprobe centec_at24c64
|
||||
modprobe at24
|
||||
echo 24c64 0x57 > /sys/bus/i2c/devices/i2c-0/new_device
|
||||
modprobe centec_e582_48x6q_platform
|
||||
|
||||
#start platform monitor
|
||||
@ -53,9 +54,9 @@ elif [ "$1" == "deinit" ]; then
|
||||
kill -9 $(pidof platform_monitor) > /dev/null 2>&1
|
||||
rm -rf /usr/bin/platform_monitor
|
||||
modprobe -r centec_e582_48x6q_platform
|
||||
modprobe -r centec_at24c64
|
||||
modprobe -r at24
|
||||
modprobe -r dal
|
||||
modprobe -r i2c-mux-pca954x
|
||||
modprobe -r ctc-i2c-mux-pca954x
|
||||
modprobe -r i2c-dev
|
||||
else
|
||||
echo "e582-48x6q_platform : Invalid option !"
|
||||
|
@ -7,11 +7,11 @@ Standards-Version: 3.9.3
|
||||
|
||||
Package: platform-modules-e582-48x2q4z
|
||||
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
|
||||
|
||||
Package: platform-modules-e582-48x6q
|
||||
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
|
||||
|
||||
|
@ -6,6 +6,7 @@ KVERSION ?= $(shell uname -r)
|
||||
KERNEL_SRC := /lib/modules/$(KVERSION)
|
||||
MOD_SRC_DIR:= $(shell pwd)
|
||||
MODULE_DIRS:= 48x6q 48x2q4z
|
||||
PCA954X_DIR := pca954x
|
||||
|
||||
%:
|
||||
dh $@
|
||||
@ -14,6 +15,9 @@ override_dh_auto_build:
|
||||
(for mod in $(MODULE_DIRS); do \
|
||||
make -C $(KERNEL_SRC)/build M=$(MOD_SRC_DIR)/$${mod}/modules; \
|
||||
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
|
||||
cp $(MOD_SRC_DIR)/../centec-dal $(MOD_SRC_DIR)/centec-dal -rf
|
||||
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 \
|
||||
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)
|
||||
|
||||
override_dh_usrlocal:
|
||||
|
@ -0,0 +1 @@
|
||||
obj-m := ctc-i2c-mux-pca954x.o
|
@ -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");
|
@ -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 */
|
@ -7,6 +7,6 @@ Standards-Version: 3.9.3
|
||||
|
||||
Package: platform-modules-embedway-es6220
|
||||
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
|
||||
|
||||
|
@ -20,12 +20,16 @@ generate_version_file()
|
||||
|
||||
if [ "$ENABLE_VERSION_CONTROL_DEB" != "y" ]; 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
|
||||
sudo mkdir -p $FILESYSTEM_ROOT/usr/bin
|
||||
sudo cp /usr/bin/qemu*static $FILESYSTEM_ROOT/usr/bin || true
|
||||
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
|
||||
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
|
||||
|
@ -3,7 +3,9 @@ FROM multiarch/qemu-user-static:x86_64-arm-5.0.0-2 as qemu
|
||||
FROM multiarch/debian-debootstrap:armhf-bullseye
|
||||
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
|
||||
{%- 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
|
||||
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
|
||||
{%- else -%}
|
||||
FROM debian:bullseye
|
||||
{%- endif %}
|
||||
@ -351,6 +353,11 @@ RUN apt-get update && apt-get install -y \
|
||||
|
||||
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
|
||||
{%- if CONFIGURED_ARCH == "armhf" %}
|
||||
# Install doxygen build dependency packages
|
||||
|
Loading…
Reference in New Issue
Block a user