Adding files #3
|
@ -28,6 +28,8 @@ make defconfig
|
|||
# Enable collectd network encryption
|
||||
echo "CONFIG_PACKAGE_COLLECTD_ENCRYPTED_NETWORK=y" >> .config
|
||||
|
||||
make package/kmod-i2c-mux-pinctrl/compile
|
||||
make package/kmod-rtc-pcf85063/compile
|
||||
make package/asterisk-chan-quectel/compile
|
||||
make package/collectd/compile
|
||||
# no signing key
|
||||
|
@ -38,8 +40,12 @@ cd ${pwd}
|
|||
|
||||
tar -xJf /Downloads/openwrt-imagebuilder-${OPENWRT_VERSION}-*.tar.xz
|
||||
|
||||
PROFILE=rpi-4
|
||||
BINPATH=bin/targets/${OPENWRT_TARGET}/${OPENWRT_DEVICE}
|
||||
IMAGE_PATH=${BINPATH}/openwrt-${OPENWRT_VERSION}-${OPENWRT_TARGET}-${OPENWRT_DEVICE}-${PROFILE}
|
||||
|
||||
cd openwrt-imagebuilder-*/
|
||||
cp ${sdkdir}/bin/targets/bcm27xx/bcm2711/packages/*.ipk packages/
|
||||
cp ${sdkdir}/${BINPATH}/packages/*.ipk packages/
|
||||
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/packages/*.ipk packages/
|
||||
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/telephony/*.ipk packages/
|
||||
cp ${sdkdir}/bin/packages/aarch64_cortex-a72/quectel/*.ipk packages/
|
||||
|
@ -51,15 +57,9 @@ echo "CONFIG_TARGET_ROOTFS_PARTSIZE=308" >> .config
|
|||
# Disable ext4 images
|
||||
echo "CONFIG_TARGET_ROOTFS_EXT4FS=n" >> .config
|
||||
|
||||
# Setup files
|
||||
mkdir -p files/etc/dropbear
|
||||
chmod 0750 files/etc/dropbear
|
||||
cp ${pwd}/authorized_keys files/etc/dropbear
|
||||
chmod 0400 files/etc/dropbear/authorized_keys
|
||||
mkdir -p files/etc/uci-defaults
|
||||
cp ${pwd}/defaults/* files/etc/uci-defaults
|
||||
|
||||
PACKAGES="kmod-nf-nathelper-extra kmod-rtc-ds1307 \
|
||||
PACKAGES="kmod-i2c-bcm2835 kmod-i2c-mux kmod-i2c-mux-pinctrl \
|
||||
kmod-nf-nathelper-extra \
|
||||
kmod-rtc-ds1307 kmod-rtc-pcf8563 kmod-rtc-pcf85063 \
|
||||
kmod-usb-audio kmod-usb-net-rtl8152 \
|
||||
alsa-lib pciutils usbutils \
|
||||
kmod-usb-net-qmi-wwan libqmi qmi-utils uqmi luci-proto-qmi \
|
||||
|
@ -87,7 +87,7 @@ PACKAGES="kmod-nf-nathelper-extra kmod-rtc-ds1307 \
|
|||
collectd-mod-thermal collectd-mod-uptime \
|
||||
collectd-mod-users collectd-mod-vmem \
|
||||
collectd-mod-wireless \
|
||||
curl etherwake fail2ban \
|
||||
curl etherwake fail2ban hwclock i2c-tools \
|
||||
luci-app-mwan3 luci-app-upnp \
|
||||
luci-proto-wireguard \
|
||||
luci-ssl-nginx luci-app-acme \
|
||||
|
@ -97,13 +97,61 @@ PACKAGES="kmod-nf-nathelper-extra kmod-rtc-ds1307 \
|
|||
wireguard-tools wget-ssl \
|
||||
"
|
||||
|
||||
make image PROFILE=rpi-4 \
|
||||
BOOTSOURCE=target/linux/${OPENWRT_TARGET}/image
|
||||
BOOTCONFIG=${BOOTSOURCE}/config.txt
|
||||
KERNELSOURCE=build_dir/target-aarch64_cortex-a72_musl/linux-${OPENWRT_TARGET}_${OPENWRT_DEVICE}/linux-5.10.146
|
||||
OVERLAYSOURCE=${KERNELSOURCE}/arch/arm64/boot/dts/overlays
|
||||
|
||||
# Setup files
|
||||
mkdir -p files/etc/dropbear
|
||||
chmod 0750 files/etc/dropbear
|
||||
cp ${pwd}/authorized_keys files/etc/dropbear
|
||||
chmod 0400 files/etc/dropbear/authorized_keys
|
||||
mkdir -p files/etc/uci-defaults
|
||||
cp ${pwd}/defaults/* files/etc/uci-defaults
|
||||
|
||||
cp ${pwd}/overlays/dualeth.txt ${BOOTSOURCE}/current.txt
|
||||
|
||||
cp ${BOOTCONFIG} .
|
||||
|
||||
cat << "EOF" >> ${BOOTCONFIG}
|
||||
dtparam=i2c1=on
|
||||
dtparam=spi=on
|
||||
dtparam=i2s=on
|
||||
dtoverlay=i2c-rtc,ds1307
|
||||
|
||||
include current.txt
|
||||
EOF
|
||||
|
||||
pushd ${BOOTSOURCE}
|
||||
git apply ${pwd}/patches/bootconfig-add.patch
|
||||
popd
|
||||
|
||||
make image PROFILE=${PROFILE} \
|
||||
PACKAGES="${PACKAGES}" DISABLED_SERVICES="dropbear" FILES="files"
|
||||
|
||||
make manifest PROFILE=rpi-4 PACKAGES="${PACKAGES}"
|
||||
mv ${IMAGE_PATH}-squashfs-factory.img.gz ${IMAGE_PATH}-squashfs-factory-dualeth.img.gz
|
||||
mv ${IMAGE_PATH}-squashfs-sysupgrade.img.gz ${IMAGE_PATH}-squashfs-sysupgrade-dualeth.img.gz
|
||||
|
||||
cp ${pwd}/overlays/sensing.txt ${BOOTSOURCE}/current.txt
|
||||
cp ${pwd}/overlays/*.dtbo ${OVERLAYSOURCE}
|
||||
cp config.txt ${BOOTCONFIG}
|
||||
echo "include current.txt" >> ${BOOTCONFIG}
|
||||
pushd ${KERNELSOURCE}
|
||||
git apply ${pwd}/patches/overlay-add.patch
|
||||
popd
|
||||
|
||||
make image PROFILE=${PROFILE} \
|
||||
PACKAGES="${PACKAGES}" DISABLED_SERVICES="dropbear" FILES="files"
|
||||
|
||||
mv ${IMAGE_PATH}-squashfs-factory.img.gz ${IMAGE_PATH}-squashfs-factory-sensing.img.gz
|
||||
mv ${IMAGE_PATH}-squashfs-sysupgrade.img.gz ${IMAGE_PATH}-squashfs-sysupgrade-sensing.img.gz
|
||||
|
||||
|
||||
make manifest PROFILE=${PROFILE} PACKAGES="${PACKAGES}"
|
||||
|
||||
out=../../out
|
||||
|
||||
mkdir -p ${out}
|
||||
mv bin/targets/bcm27xx/bcm2711/*.gz ${out}
|
||||
mv ${BINPATH}/*.gz ${out}
|
||||
mv packages ${out}
|
||||
|
|
41
feed/kmod-i2c-mux-pinctrl/Makefile
Normal file
41
feed/kmod-i2c-mux-pinctrl/Makefile
Normal file
|
@ -0,0 +1,41 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=i2c-mux-pinctrl
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define KernelPackage/$(PKG_NAME)
|
||||
SUBMENU:=I2C support
|
||||
TITLE:=pinctrl-based I2C multiplexer
|
||||
DEPENDS:=+kmod-i2c-mux
|
||||
AUTOLOAD:=$(call AutoLoad,51,i2c-mux-pinctrl,1)
|
||||
FILES:=$(PKG_BUILD_DIR)/i2c-mux-pinctrl.ko
|
||||
KCONFIG:=
|
||||
endef
|
||||
|
||||
define KernelPackage/$(PKG_NAME)/description
|
||||
Kernel modules for GENERIC_PINCTRL I2C bus mux/switching devices
|
||||
endef
|
||||
|
||||
EXTRA_KCONFIG:= \
|
||||
CONFIG_I2C_MUX_PINCTRL=m
|
||||
|
||||
EXTRA_CFLAGS:= \
|
||||
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
|
||||
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
|
||||
|
||||
MAKE_OPTS:= \
|
||||
$(KERNEL_MAKE_FLAGS) \
|
||||
M="$(PKG_BUILD_DIR)" \
|
||||
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
|
||||
$(EXTRA_KCONFIG)
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C "$(LINUX_DIR)" \
|
||||
$(MAKE_OPTS) \
|
||||
modules
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,$(PKG_NAME)))
|
8
feed/kmod-i2c-mux-pinctrl/src/Makefile
Normal file
8
feed/kmod-i2c-mux-pinctrl/src/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for multiplexer I2C chip drivers.
|
||||
|
||||
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
|
||||
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
|
||||
|
||||
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
|
184
feed/kmod-i2c-mux-pinctrl/src/gpiolib.h
Normal file
184
feed/kmod-i2c-mux-pinctrl/src/gpiolib.h
Normal file
|
@ -0,0 +1,184 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Internal GPIO functions.
|
||||
*
|
||||
* Copyright (C) 2013, Intel Corporation
|
||||
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
*/
|
||||
|
||||
#ifndef GPIOLIB_H
|
||||
#define GPIOLIB_H
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */
|
||||
#include <linux/err.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cdev.h>
|
||||
|
||||
#define GPIOCHIP_NAME "gpiochip"
|
||||
|
||||
/**
|
||||
* struct gpio_device - internal state container for GPIO devices
|
||||
* @id: numerical ID number for the GPIO chip
|
||||
* @dev: the GPIO device struct
|
||||
* @chrdev: character device for the GPIO device
|
||||
* @mockdev: class device used by the deprecated sysfs interface (may be
|
||||
* NULL)
|
||||
* @owner: helps prevent removal of modules exporting active GPIOs
|
||||
* @chip: pointer to the corresponding gpiochip, holding static
|
||||
* data for this device
|
||||
* @descs: array of ngpio descriptors.
|
||||
* @ngpio: the number of GPIO lines on this GPIO device, equal to the size
|
||||
* of the @descs array.
|
||||
* @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned
|
||||
* at device creation time.
|
||||
* @label: a descriptive name for the GPIO device, such as the part number
|
||||
* or name of the IP component in a System on Chip.
|
||||
* @data: per-instance data assigned by the driver
|
||||
* @list: links gpio_device:s together for traversal
|
||||
*
|
||||
* This state container holds most of the runtime variable data
|
||||
* for a GPIO device and can hold references and live on after the
|
||||
* GPIO chip has been removed, if it is still being used from
|
||||
* userspace.
|
||||
*/
|
||||
struct gpio_device {
|
||||
int id;
|
||||
struct device dev;
|
||||
struct cdev chrdev;
|
||||
struct device *mockdev;
|
||||
struct module *owner;
|
||||
struct gpio_chip *chip;
|
||||
struct gpio_desc *descs;
|
||||
int base;
|
||||
u16 ngpio;
|
||||
const char *label;
|
||||
void *data;
|
||||
struct list_head list;
|
||||
struct blocking_notifier_head notifier;
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
/*
|
||||
* If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
|
||||
* describe the actual pin range which they serve in an SoC. This
|
||||
* information would be used by pinctrl subsystem to configure
|
||||
* corresponding pins for gpio usage.
|
||||
*/
|
||||
struct list_head pin_ranges;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* gpio suffixes used for ACPI and device tree lookup */
|
||||
static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "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[];
|
||||
};
|
||||
|
||||
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,
|
||||
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,
|
||||
struct gpio_array *array_info,
|
||||
unsigned long *value_bitmap);
|
||||
|
||||
extern spinlock_t gpio_lock;
|
||||
extern struct list_head gpio_devices;
|
||||
|
||||
struct gpio_desc {
|
||||
struct gpio_device *gdev;
|
||||
unsigned long flags;
|
||||
/* flag symbols are bit numbers */
|
||||
#define FLAG_REQUESTED 0
|
||||
#define FLAG_IS_OUT 1
|
||||
#define FLAG_EXPORT 2 /* protected by sysfs_lock */
|
||||
#define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */
|
||||
#define FLAG_ACTIVE_LOW 6 /* value has active low */
|
||||
#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);
|
||||
void gpiod_free(struct gpio_desc *desc);
|
||||
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
|
||||
unsigned long lflags, enum gpiod_flags dflags);
|
||||
int gpiod_hog(struct gpio_desc *desc, const char *name,
|
||||
unsigned long lflags, enum gpiod_flags dflags);
|
||||
|
||||
/*
|
||||
* Return the GPIO number of the passed descriptor relative to its chip
|
||||
*/
|
||||
static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
|
||||
{
|
||||
return desc - &desc->gdev->descs[0];
|
||||
}
|
||||
|
||||
/* With descriptor prefix */
|
||||
|
||||
#define gpiod_emerg(desc, fmt, ...) \
|
||||
pr_emerg("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\
|
||||
##__VA_ARGS__)
|
||||
#define gpiod_crit(desc, fmt, ...) \
|
||||
pr_crit("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
|
||||
##__VA_ARGS__)
|
||||
#define gpiod_err(desc, fmt, ...) \
|
||||
pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
|
||||
##__VA_ARGS__)
|
||||
#define gpiod_warn(desc, fmt, ...) \
|
||||
pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
|
||||
##__VA_ARGS__)
|
||||
#define gpiod_info(desc, fmt, ...) \
|
||||
pr_info("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \
|
||||
##__VA_ARGS__)
|
||||
#define gpiod_dbg(desc, fmt, ...) \
|
||||
pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\
|
||||
##__VA_ARGS__)
|
||||
|
||||
/* With chip prefix */
|
||||
|
||||
#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 */
|
243
feed/kmod-i2c-mux-pinctrl/src/i2c-mux-gpio.c
Normal file
243
feed/kmod-i2c-mux-pinctrl/src/i2c-mux-gpio.c
Normal file
|
@ -0,0 +1,243 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* I2C multiplexer using GPIO API
|
||||
*
|
||||
* Peter Korsgaard <peter.korsgaard@barco.com>
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/platform_data/i2c-mux-gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
/* FIXME: stop poking around inside gpiolib */
|
||||
#include "gpiolib.h"
|
||||
|
||||
struct gpiomux {
|
||||
struct i2c_mux_gpio_platform_data data;
|
||||
int ngpios;
|
||||
struct gpio_desc **gpios;
|
||||
};
|
||||
|
||||
static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
|
||||
{
|
||||
DECLARE_BITMAP(values, BITS_PER_TYPE(val));
|
||||
|
||||
values[0] = val;
|
||||
|
||||
gpiod_set_array_value_cansleep(mux->ngpios, mux->gpios, NULL, values);
|
||||
}
|
||||
|
||||
static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
struct gpiomux *mux = i2c_mux_priv(muxc);
|
||||
|
||||
i2c_mux_gpio_set(mux, chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
struct gpiomux *mux = i2c_mux_priv(muxc);
|
||||
|
||||
i2c_mux_gpio_set(mux, mux->data.idle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *adapter_np, *child;
|
||||
struct i2c_adapter *adapter;
|
||||
unsigned *values;
|
||||
int i = 0;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
|
||||
if (!adapter_np) {
|
||||
dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
||||
of_node_put(adapter_np);
|
||||
if (!adapter)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
mux->data.parent = i2c_adapter_id(adapter);
|
||||
put_device(&adapter->dev);
|
||||
|
||||
mux->data.n_values = of_get_child_count(np);
|
||||
|
||||
values = devm_kcalloc(&pdev->dev,
|
||||
mux->data.n_values, sizeof(*mux->data.values),
|
||||
GFP_KERNEL);
|
||||
if (!values) {
|
||||
dev_err(&pdev->dev, "Cannot allocate values array");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
of_property_read_u32(child, "reg", values + i);
|
||||
i++;
|
||||
}
|
||||
mux->data.values = values;
|
||||
|
||||
if (of_property_read_u32(np, "idle-state", &mux->data.idle))
|
||||
mux->data.idle = I2C_MUX_GPIO_NO_IDLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_mux_core *muxc;
|
||||
struct gpiomux *mux;
|
||||
struct i2c_adapter *parent;
|
||||
struct i2c_adapter *root;
|
||||
unsigned initial_state;
|
||||
int i, ngpios, ret;
|
||||
|
||||
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!dev_get_platdata(&pdev->dev)) {
|
||||
ret = i2c_mux_gpio_probe_dt(mux, pdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
memcpy(&mux->data, dev_get_platdata(&pdev->dev),
|
||||
sizeof(mux->data));
|
||||
}
|
||||
|
||||
ngpios = gpiod_count(&pdev->dev, "mux");
|
||||
if (ngpios <= 0) {
|
||||
dev_err(&pdev->dev, "no valid gpios provided\n");
|
||||
return ngpios ?: -EINVAL;
|
||||
}
|
||||
mux->ngpios = ngpios;
|
||||
|
||||
parent = i2c_get_adapter(mux->data.parent);
|
||||
if (!parent)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
|
||||
ngpios * sizeof(*mux->gpios), 0,
|
||||
i2c_mux_gpio_select, NULL);
|
||||
if (!muxc) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_failed;
|
||||
}
|
||||
mux->gpios = muxc->priv;
|
||||
muxc->priv = mux;
|
||||
|
||||
platform_set_drvdata(pdev, muxc);
|
||||
|
||||
root = i2c_root_adapter(&parent->dev);
|
||||
|
||||
muxc->mux_locked = true;
|
||||
|
||||
if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) {
|
||||
initial_state = mux->data.idle;
|
||||
muxc->deselect = i2c_mux_gpio_deselect;
|
||||
} else {
|
||||
initial_state = mux->data.values[0];
|
||||
}
|
||||
|
||||
for (i = 0; i < ngpios; i++) {
|
||||
struct device *gpio_dev;
|
||||
struct gpio_desc *gpiod;
|
||||
enum gpiod_flags flag;
|
||||
|
||||
if (initial_state & BIT(i))
|
||||
flag = GPIOD_OUT_HIGH;
|
||||
else
|
||||
flag = GPIOD_OUT_LOW;
|
||||
gpiod = devm_gpiod_get_index(&pdev->dev, "mux", i, flag);
|
||||
if (IS_ERR(gpiod)) {
|
||||
ret = PTR_ERR(gpiod);
|
||||
goto alloc_failed;
|
||||
}
|
||||
|
||||
mux->gpios[i] = gpiod;
|
||||
|
||||
if (!muxc->mux_locked)
|
||||
continue;
|
||||
|
||||
/* FIXME: find a proper way to access the GPIO device */
|
||||
gpio_dev = &gpiod->gdev->dev;
|
||||
muxc->mux_locked = i2c_root_adapter(gpio_dev) == root;
|
||||
}
|
||||
|
||||
if (muxc->mux_locked)
|
||||
dev_info(&pdev->dev, "mux-locked i2c mux\n");
|
||||
|
||||
for (i = 0; i < mux->data.n_values; i++) {
|
||||
u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
|
||||
unsigned int class = mux->data.classes ? mux->data.classes[i] : 0;
|
||||
|
||||
ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
|
||||
if (ret)
|
||||
goto add_adapter_failed;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "%d port mux on %s adapter\n",
|
||||
mux->data.n_values, parent->name);
|
||||
|
||||
return 0;
|
||||
|
||||
add_adapter_failed:
|
||||
i2c_mux_del_adapters(muxc);
|
||||
alloc_failed:
|
||||
i2c_put_adapter(parent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_mux_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_mux_del_adapters(muxc);
|
||||
i2c_put_adapter(muxc->parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_mux_gpio_of_match[] = {
|
||||
{ .compatible = "i2c-mux-gpio", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_mux_gpio_of_match);
|
||||
|
||||
static struct platform_driver i2c_mux_gpio_driver = {
|
||||
.probe = i2c_mux_gpio_probe,
|
||||
.remove = i2c_mux_gpio_remove,
|
||||
.driver = {
|
||||
.name = "i2c-mux-gpio",
|
||||
.of_match_table = i2c_mux_gpio_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(i2c_mux_gpio_driver);
|
||||
|
||||
MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver");
|
||||
MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:i2c-mux-gpio");
|
198
feed/kmod-i2c-mux-pinctrl/src/i2c-mux-pinctrl.c
Normal file
198
feed/kmod-i2c-mux-pinctrl/src/i2c-mux-pinctrl.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* I2C multiplexer using pinctrl API
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-mux.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include "pinctrl_core.h"
|
||||
|
||||
struct i2c_mux_pinctrl {
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *states[];
|
||||
};
|
||||
|
||||
static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
|
||||
|
||||
return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
|
||||
}
|
||||
|
||||
static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||
{
|
||||
return i2c_mux_pinctrl_select(muxc, muxc->num_adapters);
|
||||
}
|
||||
|
||||
static struct i2c_adapter *i2c_mux_pinctrl_root_adapter(
|
||||
struct pinctrl_state *state)
|
||||
{
|
||||
struct i2c_adapter *root = NULL;
|
||||
struct pinctrl_setting *setting;
|
||||
struct i2c_adapter *pin_root;
|
||||
|
||||
list_for_each_entry(setting, &state->settings, node) {
|
||||
pin_root = i2c_root_adapter(setting->pctldev->dev);
|
||||
if (!pin_root)
|
||||
return NULL;
|
||||
if (!root)
|
||||
root = pin_root;
|
||||
else if (root != pin_root)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *parent_np;
|
||||
struct i2c_adapter *parent;
|
||||
|
||||
parent_np = of_parse_phandle(np, "i2c-parent", 0);
|
||||
if (!parent_np) {
|
||||
dev_err(dev, "Cannot parse i2c-parent\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
parent = of_find_i2c_adapter_by_node(parent_np);
|
||||
of_node_put(parent_np);
|
||||
if (!parent)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct i2c_mux_core *muxc;
|
||||
struct i2c_mux_pinctrl *mux;
|
||||
struct i2c_adapter *parent;
|
||||
struct i2c_adapter *root;
|
||||
int num_names, i, ret;
|
||||
const char *name;
|
||||
|
||||
num_names = of_property_count_strings(np, "pinctrl-names");
|
||||
if (num_names < 0) {
|
||||
dev_err(dev, "Cannot parse pinctrl-names: %d\n",
|
||||
num_names);
|
||||
return num_names;
|
||||
}
|
||||
|
||||
parent = i2c_mux_pinctrl_parent_adapter(dev);
|
||||
if (IS_ERR(parent))
|
||||
return PTR_ERR(parent);
|
||||
|
||||
muxc = i2c_mux_alloc(parent, dev, num_names,
|
||||
struct_size(mux, states, num_names),
|
||||
0, i2c_mux_pinctrl_select, NULL);
|
||||
if (!muxc) {
|
||||
ret = -ENOMEM;
|
||||
goto err_put_parent;
|
||||
}
|
||||
mux = i2c_mux_priv(muxc);
|
||||
|
||||
platform_set_drvdata(pdev, muxc);
|
||||
|
||||
mux->pinctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR(mux->pinctrl)) {
|
||||
ret = PTR_ERR(mux->pinctrl);
|
||||
dev_err(dev, "Cannot get pinctrl: %d\n", ret);
|
||||
goto err_put_parent;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_names; i++) {
|
||||
ret = of_property_read_string_index(np, "pinctrl-names", i,
|
||||
&name);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Cannot parse pinctrl-names: %d\n", ret);
|
||||
goto err_put_parent;
|
||||
}
|
||||
|
||||
mux->states[i] = pinctrl_lookup_state(mux->pinctrl, name);
|
||||
if (IS_ERR(mux->states[i])) {
|
||||
ret = PTR_ERR(mux->states[i]);
|
||||
dev_err(dev, "Cannot look up pinctrl state %s: %d\n",
|
||||
name, ret);
|
||||
goto err_put_parent;
|
||||
}
|
||||
|
||||
if (strcmp(name, "idle"))
|
||||
continue;
|
||||
|
||||
if (i != num_names - 1) {
|
||||
dev_err(dev, "idle state must be last\n");
|
||||
ret = -EINVAL;
|
||||
goto err_put_parent;
|
||||
}
|
||||
muxc->deselect = i2c_mux_pinctrl_deselect;
|
||||
}
|
||||
|
||||
root = i2c_root_adapter(&muxc->parent->dev);
|
||||
|
||||
muxc->mux_locked = true;
|
||||
for (i = 0; i < num_names; i++) {
|
||||
if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) {
|
||||
muxc->mux_locked = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (muxc->mux_locked)
|
||||
dev_info(dev, "mux-locked i2c mux\n");
|
||||
|
||||
/* Do not add any adapter for the idle state (if it's there at all). */
|
||||
for (i = 0; i < num_names - !!muxc->deselect; i++) {
|
||||
ret = i2c_mux_add_adapter(muxc, 0, i, 0);
|
||||
if (ret)
|
||||
goto err_del_adapter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_del_adapter:
|
||||
i2c_mux_del_adapters(muxc);
|
||||
err_put_parent:
|
||||
i2c_put_adapter(parent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_mux_pinctrl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_mux_del_adapters(muxc);
|
||||
i2c_put_adapter(muxc->parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_mux_pinctrl_of_match[] = {
|
||||
{ .compatible = "i2c-mux-pinctrl", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
|
||||
|
||||
static struct platform_driver i2c_mux_pinctrl_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-mux-pinctrl",
|
||||
.of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match),
|
||||
},
|
||||
.probe = i2c_mux_pinctrl_probe,
|
||||
.remove = i2c_mux_pinctrl_remove,
|
||||
};
|
||||
module_platform_driver(i2c_mux_pinctrl_driver);
|
||||
|
||||
MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver");
|
||||
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:i2c-mux-pinctrl");
|
249
feed/kmod-i2c-mux-pinctrl/src/pinctrl_core.h
Normal file
249
feed/kmod-i2c-mux-pinctrl/src/pinctrl_core.h
Normal file
|
@ -0,0 +1,249 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Core private header for the pin control subsystem
|
||||
*
|
||||
* Copyright (C) 2011 ST-Ericsson SA
|
||||
* Written on behalf of Linaro for ST-Ericsson
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/radix-tree.h>
|
||||
#include <linux/pinctrl/pinconf.h>
|
||||
#include <linux/pinctrl/machine.h>
|
||||
|
||||
struct pinctrl_gpio_range;
|
||||
|
||||
/**
|
||||
* struct pinctrl_dev - pin control class device
|
||||
* @node: node to include this pin controller in the global pin controller list
|
||||
* @desc: the pin controller descriptor supplied when initializing this pin
|
||||
* controller
|
||||
* @pin_desc_tree: each pin descriptor for this pin controller is stored in
|
||||
* this radix tree
|
||||
* @pin_group_tree: optionally each pin group can be stored in this radix tree
|
||||
* @num_groups: optionally number of groups can be kept here
|
||||
* @pin_function_tree: optionally each function can be stored in this radix tree
|
||||
* @num_functions: optionally number of functions can be kept here
|
||||
* @gpio_ranges: a list of GPIO ranges that is handled by this pin controller,
|
||||
* ranges are added to this list at runtime
|
||||
* @dev: the device entry for this pin controller
|
||||
* @owner: module providing the pin controller, used for refcounting
|
||||
* @driver_data: driver data for drivers registering to the pin controller
|
||||
* subsystem
|
||||
* @p: result of pinctrl_get() for this device
|
||||
* @hog_default: default state for pins hogged by this device
|
||||
* @hog_sleep: sleep state for pins hogged by this device
|
||||
* @mutex: mutex taken on each pin controller specific action
|
||||
* @device_root: debugfs root for this device
|
||||
*/
|
||||
struct pinctrl_dev {
|
||||
struct list_head node;
|
||||
struct pinctrl_desc *desc;
|
||||
struct radix_tree_root pin_desc_tree;
|
||||
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
|
||||
struct radix_tree_root pin_group_tree;
|
||||
unsigned int num_groups;
|
||||
#endif
|
||||
#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS
|
||||
struct radix_tree_root pin_function_tree;
|
||||
unsigned int num_functions;
|
||||
#endif
|
||||
struct list_head gpio_ranges;
|
||||
struct device *dev;
|
||||
struct module *owner;
|
||||
void *driver_data;
|
||||
struct pinctrl *p;
|
||||
struct pinctrl_state *hog_default;
|
||||
struct pinctrl_state *hog_sleep;
|
||||
struct mutex mutex;
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *device_root;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl - per-device pin control state holder
|
||||
* @node: global list node
|
||||
* @dev: the device using this pin control handle
|
||||
* @states: a list of states for this device
|
||||
* @state: the current state
|
||||
* @dt_maps: the mapping table chunks dynamically parsed from device tree for
|
||||
* this device, if any
|
||||
* @users: reference count
|
||||
*/
|
||||
struct pinctrl {
|
||||
struct list_head node;
|
||||
struct device *dev;
|
||||
struct list_head states;
|
||||
struct pinctrl_state *state;
|
||||
struct list_head dt_maps;
|
||||
struct kref users;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_state - a pinctrl state for a device
|
||||
* @node: list node for struct pinctrl's @states field
|
||||
* @name: the name of this state
|
||||
* @settings: a list of settings for this state
|
||||
*/
|
||||
struct pinctrl_state {
|
||||
struct list_head node;
|
||||
const char *name;
|
||||
struct list_head settings;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP
|
||||
* @group: the group selector to program
|
||||
* @func: the function selector to program
|
||||
*/
|
||||
struct pinctrl_setting_mux {
|
||||
unsigned group;
|
||||
unsigned func;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_*
|
||||
* @group_or_pin: the group selector or pin ID to program
|
||||
* @configs: a pointer to an array of config parameters/values to program into
|
||||
* hardware. Each individual pin controller defines the format and meaning
|
||||
* of config parameters.
|
||||
* @num_configs: the number of entries in array @configs
|
||||
*/
|
||||
struct pinctrl_setting_configs {
|
||||
unsigned group_or_pin;
|
||||
unsigned long *configs;
|
||||
unsigned num_configs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_setting - an individual mux or config setting
|
||||
* @node: list node for struct pinctrl_settings's @settings field
|
||||
* @type: the type of setting
|
||||
* @pctldev: pin control device handling to be programmed. Not used for
|
||||
* PIN_MAP_TYPE_DUMMY_STATE.
|
||||
* @dev_name: the name of the device using this state
|
||||
* @data: Data specific to the setting type
|
||||
*/
|
||||
struct pinctrl_setting {
|
||||
struct list_head node;
|
||||
enum pinctrl_map_type type;
|
||||
struct pinctrl_dev *pctldev;
|
||||
const char *dev_name;
|
||||
union {
|
||||
struct pinctrl_setting_mux mux;
|
||||
struct pinctrl_setting_configs configs;
|
||||
} data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pin_desc - pin descriptor for each physical pin in the arch
|
||||
* @pctldev: corresponding pin control device
|
||||
* @name: a name for the pin, e.g. the name of the pin/pad/finger on a
|
||||
* datasheet or such
|
||||
* @dynamic_name: if the name of this pin was dynamically allocated
|
||||
* @drv_data: driver-defined per-pin data. pinctrl core does not touch this
|
||||
* @mux_usecount: If zero, the pin is not claimed, and @owner should be NULL.
|
||||
* If non-zero, this pin is claimed by @owner. This field is an integer
|
||||
* rather than a boolean, since pinctrl_get() might process multiple
|
||||
* mapping table entries that refer to, and hence claim, the same group
|
||||
* or pin, and each of these will increment the @usecount.
|
||||
* @mux_owner: The name of device that called pinctrl_get().
|
||||
* @mux_setting: The most recent selected mux setting for this pin, if any.
|
||||
* @gpio_owner: If pinctrl_gpio_request() was called for this pin, this is
|
||||
* the name of the GPIO that "owns" this pin.
|
||||
*/
|
||||
struct pin_desc {
|
||||
struct pinctrl_dev *pctldev;
|
||||
const char *name;
|
||||
bool dynamic_name;
|
||||
void *drv_data;
|
||||
/* These fields only added when supporting pinmux drivers */
|
||||
#ifdef CONFIG_PINMUX
|
||||
unsigned mux_usecount;
|
||||
const char *mux_owner;
|
||||
const struct pinctrl_setting_mux *mux_setting;
|
||||
const char *gpio_owner;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pinctrl_maps - a list item containing part of the mapping table
|
||||
* @node: mapping table list node
|
||||
* @maps: array of mapping table entries
|
||||
* @num_maps: the number of entries in @maps
|
||||
*/
|
||||
struct pinctrl_maps {
|
||||
struct list_head node;
|
||||
const struct pinctrl_map *maps;
|
||||
unsigned num_maps;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
|
||||
|
||||
/**
|
||||
* struct group_desc - generic pin group descriptor
|
||||
* @name: name of the pin group
|
||||
* @pins: array of pins that belong to the group
|
||||
* @num_pins: number of pins in the group
|
||||
* @data: pin controller driver specific data
|
||||
*/
|
||||
struct group_desc {
|
||||
const char *name;
|
||||
int *pins;
|
||||
int num_pins;
|
||||
void *data;
|
||||
};
|
||||
|
||||
int pinctrl_generic_get_group_count(struct pinctrl_dev *pctldev);
|
||||
|
||||
const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev,
|
||||
unsigned int group_selector);
|
||||
|
||||
int pinctrl_generic_get_group_pins(struct pinctrl_dev *pctldev,
|
||||
unsigned int group_selector,
|
||||
const unsigned int **pins,
|
||||
unsigned int *npins);
|
||||
|
||||
struct group_desc *pinctrl_generic_get_group(struct pinctrl_dev *pctldev,
|
||||
unsigned int group_selector);
|
||||
|
||||
int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name,
|
||||
int *gpins, int ngpins, void *data);
|
||||
|
||||
int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev,
|
||||
unsigned int group_selector);
|
||||
|
||||
#endif /* CONFIG_GENERIC_PINCTRL_GROUPS */
|
||||
|
||||
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
|
||||
struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np);
|
||||
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name);
|
||||
const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin);
|
||||
int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
|
||||
const char *pin_group);
|
||||
|
||||
static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev,
|
||||
unsigned int pin)
|
||||
{
|
||||
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);
|
||||
|
||||
extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev);
|
||||
extern int pinctrl_force_default(struct pinctrl_dev *pctldev);
|
||||
|
||||
extern struct mutex pinctrl_maps_mutex;
|
||||
extern struct list_head pinctrl_maps;
|
||||
|
||||
#define for_each_maps(_maps_node_, _i_, _map_) \
|
||||
list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
|
||||
for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
|
||||
_i_ < _maps_node_->num_maps; \
|
||||
_i_++, _map_ = &_maps_node_->maps[_i_])
|
42
feed/kmod-rtc-pcf85063/Makefile
Normal file
42
feed/kmod-rtc-pcf85063/Makefile
Normal file
|
@ -0,0 +1,42 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=rtc-pcf85063
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define KernelPackage/$(PKG_NAME)
|
||||
SUBMENU:=Other modules
|
||||
TITLE:=Philips PCF85063 RTC support
|
||||
DEPENDS:=+kmod-i2c-core +kmod-regmap-i2c
|
||||
AUTOLOAD:=$(call AutoProbe,rtc-pcf85063)
|
||||
FILES:=$(PKG_BUILD_DIR)/rtc-pcf85063.ko
|
||||
KCONFIG:=
|
||||
endef
|
||||
|
||||
define KernelPackage/$(PKG_NAME)/description
|
||||
Kernel module for Philips PCF85063 RTC chip.
|
||||
endef
|
||||
|
||||
EXTRA_KCONFIG:= \
|
||||
CONFIG_RTC_DRV_PCF85063=m \
|
||||
CONFIG_RTC_CLASS=y
|
||||
|
||||
EXTRA_CFLAGS:= \
|
||||
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
|
||||
$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
|
||||
|
||||
MAKE_OPTS:= \
|
||||
$(KERNEL_MAKE_FLAGS) \
|
||||
M="$(PKG_BUILD_DIR)" \
|
||||
EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
|
||||
$(EXTRA_KCONFIG)
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C "$(LINUX_DIR)" \
|
||||
$(MAKE_OPTS) \
|
||||
modules
|
||||
endef
|
||||
|
||||
$(eval $(call KernelPackage,$(PKG_NAME)))
|
8
feed/kmod-rtc-pcf85063/src/Makefile
Normal file
8
feed/kmod-rtc-pcf85063/src/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for RTC class/drivers.
|
||||
#
|
||||
|
||||
ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
|
642
feed/kmod-rtc-pcf85063/src/rtc-pcf85063.c
Normal file
642
feed/kmod-rtc-pcf85063/src/rtc-pcf85063.c
Normal file
|
@ -0,0 +1,642 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* An I2C driver for the PCF85063 RTC
|
||||
* Copyright 2014 Rose Technology
|
||||
*
|
||||
* Author: Søren Andersen <san@rosetechnology.dk>
|
||||
* Maintainers: http://www.nslu2-linux.org/
|
||||
*
|
||||
* Copyright (C) 2019 Micro Crystal AG
|
||||
* Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
*/
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/*
|
||||
* Information for this driver was pulled from the following datasheets.
|
||||
*
|
||||
* https://www.nxp.com/documents/data_sheet/PCF85063A.pdf
|
||||
* https://www.nxp.com/documents/data_sheet/PCF85063TP.pdf
|
||||
*
|
||||
* PCF85063A -- Rev. 6 — 18 November 2015
|
||||
* PCF85063TP -- Rev. 4 — 6 May 2015
|
||||
*
|
||||
* https://www.microcrystal.com/fileadmin/Media/Products/RTC/App.Manual/RV-8263-C7_App-Manual.pdf
|
||||
* RV8263 -- Rev. 1.0 — January 2019
|
||||
*/
|
||||
|
||||
#define PCF85063_REG_CTRL1 0x00 /* status */
|
||||
#define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
|
||||
#define PCF85063_REG_CTRL1_STOP BIT(5)
|
||||
|
||||
#define PCF85063_REG_CTRL2 0x01
|
||||
#define PCF85063_CTRL2_AF BIT(6)
|
||||
#define PCF85063_CTRL2_AIE BIT(7)
|
||||
|
||||
#define PCF85063_REG_OFFSET 0x02
|
||||
#define PCF85063_OFFSET_SIGN_BIT 6 /* 2's complement sign bit */
|
||||
#define PCF85063_OFFSET_MODE BIT(7)
|
||||
#define PCF85063_OFFSET_STEP0 4340
|
||||
#define PCF85063_OFFSET_STEP1 4069
|
||||
|
||||
#define PCF85063_REG_CLKO_F_MASK 0x07 /* frequency mask */
|
||||
#define PCF85063_REG_CLKO_F_32768HZ 0x00
|
||||
#define PCF85063_REG_CLKO_F_OFF 0x07
|
||||
|
||||
#define PCF85063_REG_RAM 0x03
|
||||
|
||||
#define PCF85063_REG_SC 0x04 /* datetime */
|
||||
#define PCF85063_REG_SC_OS 0x80
|
||||
|
||||
#define PCF85063_REG_ALM_S 0x0b
|
||||
#define PCF85063_AEN BIT(7)
|
||||
|
||||
struct pcf85063_config {
|
||||
struct regmap_config regmap;
|
||||
unsigned has_alarms:1;
|
||||
unsigned force_cap_7000:1;
|
||||
};
|
||||
|
||||
struct pcf85063 {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw clkout_hw;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
int rc;
|
||||
u8 regs[7];
|
||||
|
||||
/*
|
||||
* while reading, the time/date registers are blocked and not updated
|
||||
* anymore until the access is finished. To not lose a second
|
||||
* event, the access must be finished within one second. So, read all
|
||||
* time/date registers in one turn.
|
||||
*/
|
||||
rc = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_SC, regs,
|
||||
sizeof(regs));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* if the clock has lost its power it makes no sense to use its time */
|
||||
if (regs[0] & PCF85063_REG_SC_OS) {
|
||||
dev_warn(&pcf85063->rtc->dev, "Power loss detected, invalid time\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(regs[0] & 0x7F);
|
||||
tm->tm_min = bcd2bin(regs[1] & 0x7F);
|
||||
tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */
|
||||
tm->tm_mday = bcd2bin(regs[3] & 0x3F);
|
||||
tm->tm_wday = regs[4] & 0x07;
|
||||
tm->tm_mon = bcd2bin(regs[5] & 0x1F) - 1; /* rtc mn 1-12 */
|
||||
tm->tm_year = bcd2bin(regs[6]);
|
||||
tm->tm_year += 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
int rc;
|
||||
u8 regs[7];
|
||||
|
||||
/*
|
||||
* to accurately set the time, reset the divider chain and keep it in
|
||||
* reset state until all time/date registers are written
|
||||
*/
|
||||
rc = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
|
||||
PCF85063_REG_CTRL1_STOP,
|
||||
PCF85063_REG_CTRL1_STOP);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* hours, minutes and seconds */
|
||||
regs[0] = bin2bcd(tm->tm_sec) & 0x7F; /* clear OS flag */
|
||||
|
||||
regs[1] = bin2bcd(tm->tm_min);
|
||||
regs[2] = bin2bcd(tm->tm_hour);
|
||||
|
||||
/* Day of month, 1 - 31 */
|
||||
regs[3] = bin2bcd(tm->tm_mday);
|
||||
|
||||
/* Day, 0 - 6 */
|
||||
regs[4] = tm->tm_wday & 0x07;
|
||||
|
||||
/* month, 1 - 12 */
|
||||
regs[5] = bin2bcd(tm->tm_mon + 1);
|
||||
|
||||
/* year and century */
|
||||
regs[6] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
/* write all registers at once */
|
||||
rc = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_SC,
|
||||
regs, sizeof(regs));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Write the control register as a separate action since the size of
|
||||
* the register space is different between the PCF85063TP and
|
||||
* PCF85063A devices. The rollover point can not be used.
|
||||
*/
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
|
||||
PCF85063_REG_CTRL1_STOP, 0);
|
||||
}
|
||||
|
||||
static int pcf85063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
u8 buf[4];
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_ALM_S,
|
||||
buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alrm->time.tm_sec = bcd2bin(buf[0]);
|
||||
alrm->time.tm_min = bcd2bin(buf[1]);
|
||||
alrm->time.tm_hour = bcd2bin(buf[2]);
|
||||
alrm->time.tm_mday = bcd2bin(buf[3]);
|
||||
|
||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alrm->enabled = !!(val & PCF85063_CTRL2_AIE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
u8 buf[5];
|
||||
int ret;
|
||||
|
||||
buf[0] = bin2bcd(alrm->time.tm_sec);
|
||||
buf[1] = bin2bcd(alrm->time.tm_min);
|
||||
buf[2] = bin2bcd(alrm->time.tm_hour);
|
||||
buf[3] = bin2bcd(alrm->time.tm_mday);
|
||||
buf[4] = PCF85063_AEN; /* Do not match on week day */
|
||||
|
||||
ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_ALM_S,
|
||||
buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
|
||||
alrm->enabled ? PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF : PCF85063_CTRL2_AF);
|
||||
}
|
||||
|
||||
static int pcf85063_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AIE,
|
||||
enabled ? PCF85063_CTRL2_AIE : 0);
|
||||
}
|
||||
|
||||
static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_id;
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
|
||||
if (err)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (val & PCF85063_CTRL2_AF) {
|
||||
rtc_update_irq(pcf85063->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
|
||||
0);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int pcf85063_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
long val;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_OFFSET, ®);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = sign_extend32(reg & ~PCF85063_OFFSET_MODE,
|
||||
PCF85063_OFFSET_SIGN_BIT);
|
||||
|
||||
if (reg & PCF85063_OFFSET_MODE)
|
||||
*offset = val * PCF85063_OFFSET_STEP1;
|
||||
else
|
||||
*offset = val * PCF85063_OFFSET_STEP0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf85063_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
s8 mode0, mode1, reg;
|
||||
unsigned int error0, error1;
|
||||
|
||||
if (offset > PCF85063_OFFSET_STEP0 * 63)
|
||||
return -ERANGE;
|
||||
if (offset < PCF85063_OFFSET_STEP0 * -64)
|
||||
return -ERANGE;
|
||||
|
||||
mode0 = DIV_ROUND_CLOSEST(offset, PCF85063_OFFSET_STEP0);
|
||||
mode1 = DIV_ROUND_CLOSEST(offset, PCF85063_OFFSET_STEP1);
|
||||
|
||||
error0 = abs(offset - (mode0 * PCF85063_OFFSET_STEP0));
|
||||
error1 = abs(offset - (mode1 * PCF85063_OFFSET_STEP1));
|
||||
if (mode1 > 63 || mode1 < -64 || error0 < error1)
|
||||
reg = mode0 & ~PCF85063_OFFSET_MODE;
|
||||
else
|
||||
reg = mode1 | PCF85063_OFFSET_MODE;
|
||||
|
||||
return regmap_write(pcf85063->regmap, PCF85063_REG_OFFSET, reg);
|
||||
}
|
||||
|
||||
static int pcf85063_ioctl(struct device *dev, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
int status, ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_VL_READ:
|
||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_SC, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
status = status & PCF85063_REG_SC_OS ? RTC_VL_DATA_INVALID : 0;
|
||||
|
||||
return put_user(status, (unsigned int __user *)arg);
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops pcf85063_rtc_ops = {
|
||||
.read_time = pcf85063_rtc_read_time,
|
||||
.set_time = pcf85063_rtc_set_time,
|
||||
.read_offset = pcf85063_read_offset,
|
||||
.set_offset = pcf85063_set_offset,
|
||||
.ioctl = pcf85063_ioctl,
|
||||
};
|
||||
|
||||
static const struct rtc_class_ops pcf85063_rtc_ops_alarm = {
|
||||
.read_time = pcf85063_rtc_read_time,
|
||||
.set_time = pcf85063_rtc_set_time,
|
||||
.read_offset = pcf85063_read_offset,
|
||||
.set_offset = pcf85063_set_offset,
|
||||
.read_alarm = pcf85063_rtc_read_alarm,
|
||||
.set_alarm = pcf85063_rtc_set_alarm,
|
||||
.alarm_irq_enable = pcf85063_rtc_alarm_irq_enable,
|
||||
.ioctl = pcf85063_ioctl,
|
||||
};
|
||||
|
||||
static int pcf85063_nvmem_read(void *priv, unsigned int offset,
|
||||
void *val, size_t bytes)
|
||||
{
|
||||
return regmap_read(priv, PCF85063_REG_RAM, val);
|
||||
}
|
||||
|
||||
static int pcf85063_nvmem_write(void *priv, unsigned int offset,
|
||||
void *val, size_t bytes)
|
||||
{
|
||||
return regmap_write(priv, PCF85063_REG_RAM, *(u8 *)val);
|
||||
}
|
||||
|
||||
static int pcf85063_load_capacitance(struct pcf85063 *pcf85063,
|
||||
const struct device_node *np,
|
||||
unsigned int force_cap)
|
||||
{
|
||||
u32 load = 7000;
|
||||
u8 reg = 0;
|
||||
|
||||
if (force_cap)
|
||||
load = force_cap;
|
||||
else
|
||||
of_property_read_u32(np, "quartz-load-femtofarads", &load);
|
||||
|
||||
switch (load) {
|
||||
default:
|
||||
dev_warn(&pcf85063->rtc->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000",
|
||||
load);
|
||||
fallthrough;
|
||||
case 7000:
|
||||
break;
|
||||
case 12500:
|
||||
reg = PCF85063_REG_CTRL1_CAP_SEL;
|
||||
break;
|
||||
}
|
||||
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL1,
|
||||
PCF85063_REG_CTRL1_CAP_SEL, reg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
/*
|
||||
* Handling of the clkout
|
||||
*/
|
||||
|
||||
#define clkout_hw_to_pcf85063(_hw) container_of(_hw, struct pcf85063, clkout_hw)
|
||||
|
||||
static int clkout_rates[] = {
|
||||
32768,
|
||||
16384,
|
||||
8192,
|
||||
4096,
|
||||
2048,
|
||||
1024,
|
||||
1,
|
||||
0
|
||||
};
|
||||
|
||||
static unsigned long pcf85063_clkout_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw);
|
||||
unsigned int buf;
|
||||
int ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &buf);
|
||||
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
buf &= PCF85063_REG_CLKO_F_MASK;
|
||||
return clkout_rates[buf];
|
||||
}
|
||||
|
||||
static long pcf85063_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
|
||||
if (clkout_rates[i] <= rate)
|
||||
return clkout_rates[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf85063_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
|
||||
if (clkout_rates[i] == rate)
|
||||
return regmap_update_bits(pcf85063->regmap,
|
||||
PCF85063_REG_CTRL2,
|
||||
PCF85063_REG_CLKO_F_MASK, i);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int pcf85063_clkout_control(struct clk_hw *hw, bool enable)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw);
|
||||
unsigned int buf;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_OFFSET, &buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf &= PCF85063_REG_CLKO_F_MASK;
|
||||
|
||||
if (enable) {
|
||||
if (buf == PCF85063_REG_CLKO_F_OFF)
|
||||
buf = PCF85063_REG_CLKO_F_32768HZ;
|
||||
else
|
||||
return 0;
|
||||
} else {
|
||||
if (buf != PCF85063_REG_CLKO_F_OFF)
|
||||
buf = PCF85063_REG_CLKO_F_OFF;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_REG_CLKO_F_MASK, buf);
|
||||
}
|
||||
|
||||
static int pcf85063_clkout_prepare(struct clk_hw *hw)
|
||||
{
|
||||
return pcf85063_clkout_control(hw, 1);
|
||||
}
|
||||
|
||||
static void pcf85063_clkout_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
pcf85063_clkout_control(hw, 0);
|
||||
}
|
||||
|
||||
static int pcf85063_clkout_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = clkout_hw_to_pcf85063(hw);
|
||||
unsigned int buf;
|
||||
int ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &buf);
|
||||
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
return (buf & PCF85063_REG_CLKO_F_MASK) != PCF85063_REG_CLKO_F_OFF;
|
||||
}
|
||||
|
||||
static const struct clk_ops pcf85063_clkout_ops = {
|
||||
.prepare = pcf85063_clkout_prepare,
|
||||
.unprepare = pcf85063_clkout_unprepare,
|
||||
.is_prepared = pcf85063_clkout_is_prepared,
|
||||
.recalc_rate = pcf85063_clkout_recalc_rate,
|
||||
.round_rate = pcf85063_clkout_round_rate,
|
||||
.set_rate = pcf85063_clkout_set_rate,
|
||||
};
|
||||
|
||||
static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
struct device_node *node = pcf85063->rtc->dev.parent->of_node;
|
||||
|
||||
init.name = "pcf85063-clkout";
|
||||
init.ops = &pcf85063_clkout_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
pcf85063->clkout_hw.init = &init;
|
||||
|
||||
/* optional override of the clockname */
|
||||
of_property_read_string(node, "clock-output-names", &init.name);
|
||||
|
||||
/* register the clock */
|
||||
clk = devm_clk_register(&pcf85063->rtc->dev, &pcf85063->clkout_hw);
|
||||
|
||||
if (!IS_ERR(clk))
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
|
||||
return clk;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct pcf85063_config pcf85063a_config = {
|
||||
.regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x11,
|
||||
},
|
||||
.has_alarms = 1,
|
||||
};
|
||||
|
||||
static const struct pcf85063_config pcf85063tp_config = {
|
||||
.regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x0a,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pcf85063_config rv8263_config = {
|
||||
.regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x11,
|
||||
},
|
||||
.has_alarms = 1,
|
||||
.force_cap_7000 = 1,
|
||||
};
|
||||
|
||||
static int pcf85063_probe(struct i2c_client *client)
|
||||
{
|
||||
struct pcf85063 *pcf85063;
|
||||
unsigned int tmp;
|
||||
int err;
|
||||
const struct pcf85063_config *config = &pcf85063tp_config;
|
||||
const void *data = of_device_get_match_data(&client->dev);
|
||||
struct nvmem_config nvmem_cfg = {
|
||||
.name = "pcf85063_nvram",
|
||||
.reg_read = pcf85063_nvmem_read,
|
||||
.reg_write = pcf85063_nvmem_write,
|
||||
.type = NVMEM_TYPE_BATTERY_BACKED,
|
||||
.size = 1,
|
||||
};
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
|
||||
GFP_KERNEL);
|
||||
if (!pcf85063)
|
||||
return -ENOMEM;
|
||||
|
||||
if (data)
|
||||
config = data;
|
||||
|
||||
pcf85063->regmap = devm_regmap_init_i2c(client, &config->regmap);
|
||||
if (IS_ERR(pcf85063->regmap))
|
||||
return PTR_ERR(pcf85063->regmap);
|
||||
|
||||
i2c_set_clientdata(client, pcf85063);
|
||||
|
||||
err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL1, &tmp);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "RTC chip is not present\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pcf85063->rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(pcf85063->rtc))
|
||||
return PTR_ERR(pcf85063->rtc);
|
||||
|
||||
err = pcf85063_load_capacitance(pcf85063, client->dev.of_node,
|
||||
config->force_cap_7000 ? 7000 : 0);
|
||||
if (err < 0)
|
||||
dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
|
||||
err);
|
||||
|
||||
pcf85063->rtc->ops = &pcf85063_rtc_ops;
|
||||
pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
pcf85063->rtc->uie_unsupported = 1;
|
||||
|
||||
if (config->has_alarms && client->irq > 0) {
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, pcf85063_rtc_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"pcf85063", pcf85063);
|
||||
if (err) {
|
||||
dev_warn(&pcf85063->rtc->dev,
|
||||
"unable to request IRQ, alarms disabled\n");
|
||||
} else {
|
||||
pcf85063->rtc->ops = &pcf85063_rtc_ops_alarm;
|
||||
device_init_wakeup(&client->dev, true);
|
||||
err = dev_pm_set_wake_irq(&client->dev, client->irq);
|
||||
if (err)
|
||||
dev_err(&pcf85063->rtc->dev,
|
||||
"failed to enable irq wake\n");
|
||||
}
|
||||
}
|
||||
|
||||
nvmem_cfg.priv = pcf85063->regmap;
|
||||
rtc_nvmem_register(pcf85063->rtc, &nvmem_cfg);
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
/* register clk in common clk framework */
|
||||
pcf85063_clkout_register_clk(pcf85063);
|
||||
#endif
|
||||
|
||||
return rtc_register_device(pcf85063->rtc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pcf85063_of_match[] = {
|
||||
{ .compatible = "nxp,pcf85063", .data = &pcf85063tp_config },
|
||||
{ .compatible = "nxp,pcf85063tp", .data = &pcf85063tp_config },
|
||||
{ .compatible = "nxp,pcf85063a", .data = &pcf85063a_config },
|
||||
{ .compatible = "microcrystal,rv8263", .data = &rv8263_config },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcf85063_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver pcf85063_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-pcf85063",
|
||||
.of_match_table = of_match_ptr(pcf85063_of_match),
|
||||
},
|
||||
.probe_new = pcf85063_probe,
|
||||
};
|
||||
|
||||
module_i2c_driver(pcf85063_driver);
|
||||
|
||||
MODULE_AUTHOR("Søren Andersen <san@rosetechnology.dk>");
|
||||
MODULE_DESCRIPTION("PCF85063 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
7
overlays/dualeth.txt
Normal file
7
overlays/dualeth.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
dtparam=i2c_vc=on
|
||||
dtoverlay=i2c-rtc,pcf85063a,i2c_csi_dsi
|
||||
|
||||
[all]
|
||||
|
||||
[cm4]
|
||||
otg_mode=1
|
18
overlays/sensing.txt
Normal file
18
overlays/sensing.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
dtparam=i2c_arm=on
|
||||
enable_uart=1
|
||||
|
||||
dtoverlay=ed-sdhost
|
||||
dtoverlay=i2c-rtc,pcf8563
|
||||
|
||||
dtoverlay=spi1-1cs
|
||||
dtparam=cs0_pin=18,cs0_spidev=disabled
|
||||
dtoverlay=ed-mcp2515-spi1-can0
|
||||
dtoverlay=uart2
|
||||
dtoverlay=uart3
|
||||
dtoverlay=uart4
|
||||
dtoverlay=uart5
|
||||
gpio=11=op,dl
|
||||
[all]
|
||||
|
||||
[cm4]
|
||||
otg_mode=1
|
10
patches/bootconfig-add.patch
Normal file
10
patches/bootconfig-add.patch
Normal file
|
@ -0,0 +1,10 @@
|
|||
--- a/Makefile 2023-05-17 03:13:19.345089802 +0300
|
||||
+++ b/Makefile 2023-05-17 03:15:55.564872084 +0300
|
||||
@@ -22,6 +22,7 @@
|
||||
mcopy -i $@.boot cmdline.txt ::
|
||||
mcopy -i $@.boot config.txt ::
|
||||
mcopy -i $@.boot distroconfig.txt ::
|
||||
+ mcopy -i $@.boot current.txt ::
|
||||
mcopy -i $@.boot $(IMAGE_KERNEL) ::$(KERNEL_IMG)
|
||||
$(foreach dts,$(shell echo $(DEVICE_DTS)),mcopy -i $@.boot $(DTS_DIR)/$(dts).dtb ::;)
|
||||
mmd -i $@.boot ::/overlays
|
11
patches/overlay-add.patch
Normal file
11
patches/overlay-add.patch
Normal file
|
@ -0,0 +1,11 @@
|
|||
--- a/arch/arm64/boot/dts/overlays/Makefile 2023-02-24 04:54:15.978815530 +0200
|
||||
+++ b/arch/arm64/boot/dts/overlays/Makefile 2023-02-24 04:57:05.155496795 +0200
|
||||
@@ -3,6 +3,8 @@
|
||||
dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb
|
||||
|
||||
dtbo-$(CONFIG_ARCH_BCM2835) += \
|
||||
+ ed-mcp2515-spi1-can0.dtbo \
|
||||
+ ed-sdhost.dtbo \
|
||||
act-led.dtbo \
|
||||
adafruit18.dtbo \
|
||||
adau1977-adc.dtbo \
|
Loading…
Reference in New Issue
Block a user