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}
|
||||
PeterSurda
commented
this also seems to work ok this also seems to work ok
lee.miller
commented
The cause seems to be in the line 124. There should be The cause seems to be in the line 124. There should be `${pwd}/overlays/sensing.txt`.
|
||||
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
this seems to work now