【驱动篇】龙芯LS2K0300之RTC设备驱动

news2024/11/15 1:44:19

实验介绍

本次实验是关于pcf8563 RTC模块的驱动移植,大致流程如下:

  • 注册i2c设备驱动
  • 编写RTC设备驱动
  • 将device和driver驱动部署到开发板并装载,通过hwclock命令来测试

模块连接

VCC接Pin2,GND接Pin1,SCL接Pin16,SDA接Pin18

在这里插入图片描述

RTC模块引脚连接关系表

模块引脚GPIO
VCC2/
GND1/
SCL1648
SDA1849

i2c-tools查看pcf8563设备地址,地址为0x51

在这里插入图片描述

驱动代码

pcf8563_dev.c

注册i2c_client设备,设备ID为loongson,pcf8563

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>

static struct i2c_board_info    pcf8563;
static struct i2c_client *      client;

#define DEV_ID_NAME "loongson,pcf8563"

static const unsigned short addrs[] = {0x51, I2C_CLIENT_END};

static int dev_init(void)
{
    struct i2c_adapter *adapter = NULL;

    memset(&pcf8563, 0, sizeof(struct i2c_board_info));
    strlcpy(pcf8563.type, DEV_ID_NAME, I2C_NAME_SIZE);

    adapter = i2c_get_adapter(0);
 
    client = i2c_new_probed_device(adapter, &pcf8563, addrs, NULL);

    i2c_put_adapter(adapter);

    if (client)
    {
        return 0;
    }
    else
    {
        return -ENODEV;
    }
}

static void dev_exit(void)
{
    i2c_unregister_device(client);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

pcf8563_drv.c

以下驱动来自Linux内核,只需要把i2c_device_id改成loongson,pcf8563就可以了

/*
 * An I2C driver for the Philips PCF8563 RTC
 * Copyright 2005-06 Tower Technologies
 *
 * Author: Alessandro Zummo <a.zummo@towertech.it>
 * Maintainers: http://www.nslu2-linux.org/
 *
 * based on the other drivers in this same directory.
 *
 * http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/clk-provider.h>
#include <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/err.h>

#define PCF8563_REG_ST1		0x00 /* status */
#define PCF8563_REG_ST2		0x01
#define PCF8563_BIT_AIE		(1 << 1)
#define PCF8563_BIT_AF		(1 << 3)
#define PCF8563_BITS_ST2_N	(7 << 5)

#define PCF8563_REG_SC		0x02 /* datetime */
#define PCF8563_REG_MN		0x03
#define PCF8563_REG_HR		0x04
#define PCF8563_REG_DM		0x05
#define PCF8563_REG_DW		0x06
#define PCF8563_REG_MO		0x07
#define PCF8563_REG_YR		0x08

#define PCF8563_REG_AMN		0x09 /* alarm */

#define PCF8563_REG_CLKO		0x0D /* clock out */
#define PCF8563_REG_CLKO_FE		0x80 /* clock out enabled */
#define PCF8563_REG_CLKO_F_MASK		0x03 /* frequenc mask */
#define PCF8563_REG_CLKO_F_32768HZ	0x00
#define PCF8563_REG_CLKO_F_1024HZ	0x01
#define PCF8563_REG_CLKO_F_32HZ		0x02
#define PCF8563_REG_CLKO_F_1HZ		0x03

#define PCF8563_REG_TMRC	0x0E /* timer control */
#define PCF8563_TMRC_ENABLE	BIT(7)
#define PCF8563_TMRC_4096	0
#define PCF8563_TMRC_64		1
#define PCF8563_TMRC_1		2
#define PCF8563_TMRC_1_60	3
#define PCF8563_TMRC_MASK	3

#define PCF8563_REG_TMR		0x0F /* timer */

#define PCF8563_SC_LV		0x80 /* low voltage */
#define PCF8563_MO_C		0x80 /* century */

static struct i2c_driver pcf8563_driver;

struct pcf8563 {
	struct rtc_device *rtc;
	/*
	 * The meaning of MO_C bit varies by the chip type.
	 * From PCF8563 datasheet: this bit is toggled when the years
	 * register overflows from 99 to 00
	 *   0 indicates the century is 20xx
	 *   1 indicates the century is 19xx
	 * From RTC8564 datasheet: this bit indicates change of
	 * century. When the year digit data overflows from 99 to 00,
	 * this bit is set. By presetting it to 0 while still in the
	 * 20th century, it will be set in year 2000, ...
	 * There seems no reliable way to know how the system use this
	 * bit.  So let's do it heuristically, assuming we are live in
	 * 1970...2069.
	 */
	int c_polarity;	/* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
	int voltage_low; /* incicates if a low_voltage was detected */

	struct i2c_client *client;
#ifdef CONFIG_COMMON_CLK
	struct clk_hw		clkout_hw;
#endif
};

static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg,
				   unsigned char length, unsigned char *buf)
{
	struct i2c_msg msgs[] = {
		{/* setup read ptr */
			.addr = client->addr,
			.len = 1,
			.buf = &reg,
		},
		{
			.addr = client->addr,
			.flags = I2C_M_RD,
			.len = length,
			.buf = buf
		},
	};

	if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
		dev_err(&client->dev, "%s: read error\n", __func__);
		return -EIO;
	}

	return 0;
}

static int pcf8563_write_block_data(struct i2c_client *client,
				   unsigned char reg, unsigned char length,
				   unsigned char *buf)
{
	int i, err;

	for (i = 0; i < length; i++) {
		unsigned char data[2] = { reg + i, buf[i] };

		err = i2c_master_send(client, data, sizeof(data));
		if (err != sizeof(data)) {
			dev_err(&client->dev,
				"%s: err=%d addr=%02x, data=%02x\n",
				__func__, err, data[0], data[1]);
			return -EIO;
		}
	}

	return 0;
}

static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on)
{
	unsigned char buf;
	int err;

	err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf);
	if (err < 0)
		return err;

	if (on)
		buf |= PCF8563_BIT_AIE;
	else
		buf &= ~PCF8563_BIT_AIE;

	buf &= ~(PCF8563_BIT_AF | PCF8563_BITS_ST2_N);

	err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf);
	if (err < 0) {
		dev_err(&client->dev, "%s: write error\n", __func__);
		return -EIO;
	}

	return 0;
}

static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en,
				  unsigned char *pen)
{
	unsigned char buf;
	int err;

	err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf);
	if (err)
		return err;

	if (en)
		*en = !!(buf & PCF8563_BIT_AIE);
	if (pen)
		*pen = !!(buf & PCF8563_BIT_AF);

	return 0;
}

static irqreturn_t pcf8563_irq(int irq, void *dev_id)
{
	struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id);
	int err;
	char pending;

	err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending);
	if (err)
		return IRQ_NONE;

	if (pending) {
		rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF);
		pcf8563_set_alarm_mode(pcf8563->client, 1);
		return IRQ_HANDLED;
	}

	return IRQ_NONE;
}

/*
 * In the routines that deal directly with the pcf8563 hardware, we use
 * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
 */
static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
	struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
	unsigned char buf[9];
	int err;

	err = pcf8563_read_block_data(client, PCF8563_REG_ST1, 9, buf);
	if (err)
		return err;

	if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) {
		pcf8563->voltage_low = 1;
		dev_err(&client->dev,
			"low voltage detected, date/time is not reliable.\n");
		return -EINVAL;
	}

	dev_dbg(&client->dev,
		"%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
		"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
		__func__,
		buf[0], buf[1], buf[2], buf[3],
		buf[4], buf[5], buf[6], buf[7],
		buf[8]);


	tm->tm_sec = bcd2bin(buf[PCF8563_REG_SC] & 0x7F);
	tm->tm_min = bcd2bin(buf[PCF8563_REG_MN] & 0x7F);
	tm->tm_hour = bcd2bin(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
	tm->tm_mday = bcd2bin(buf[PCF8563_REG_DM] & 0x3F);
	tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
	tm->tm_mon = bcd2bin(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
	tm->tm_year = bcd2bin(buf[PCF8563_REG_YR]);
	if (tm->tm_year < 70)
		tm->tm_year += 100;	/* assume we are in 1970...2069 */
	/* detect the polarity heuristically. see note above. */
	pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ?
		(tm->tm_year >= 100) : (tm->tm_year < 100);

	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
		"mday=%d, mon=%d, year=%d, wday=%d\n",
		__func__,
		tm->tm_sec, tm->tm_min, tm->tm_hour,
		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);

	return 0;
}

static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
	struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
	unsigned char buf[9];

	dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
		"mday=%d, mon=%d, year=%d, wday=%d\n",
		__func__,
		tm->tm_sec, tm->tm_min, tm->tm_hour,
		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);

	/* hours, minutes and seconds */
	buf[PCF8563_REG_SC] = bin2bcd(tm->tm_sec);
	buf[PCF8563_REG_MN] = bin2bcd(tm->tm_min);
	buf[PCF8563_REG_HR] = bin2bcd(tm->tm_hour);

	buf[PCF8563_REG_DM] = bin2bcd(tm->tm_mday);

	/* month, 1 - 12 */
	buf[PCF8563_REG_MO] = bin2bcd(tm->tm_mon + 1);

	/* year and century */
	buf[PCF8563_REG_YR] = bin2bcd(tm->tm_year % 100);
	if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
		buf[PCF8563_REG_MO] |= PCF8563_MO_C;

	buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;

	return pcf8563_write_block_data(client, PCF8563_REG_SC,
				9 - PCF8563_REG_SC, buf + PCF8563_REG_SC);
}

#ifdef CONFIG_RTC_INTF_DEV
static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
	struct pcf8563 *pcf8563 = i2c_get_clientdata(to_i2c_client(dev));
	struct rtc_time tm;

	switch (cmd) {
	case RTC_VL_READ:
		if (pcf8563->voltage_low)
			dev_info(dev, "low voltage detected, date/time is not reliable.\n");

		if (copy_to_user((void __user *)arg, &pcf8563->voltage_low,
					sizeof(int)))
			return -EFAULT;
		return 0;
	case RTC_VL_CLR:
		/*
		 * Clear the VL bit in the seconds register in case
		 * the time has not been set already (which would
		 * have cleared it). This does not really matter
		 * because of the cached voltage_low value but do it
		 * anyway for consistency.
		 */
		if (pcf8563_get_datetime(to_i2c_client(dev), &tm))
			pcf8563_set_datetime(to_i2c_client(dev), &tm);

		/* Clear the cached value. */
		pcf8563->voltage_low = 0;

		return 0;
	default:
		return -ENOIOCTLCMD;
	}
}
#else
#define pcf8563_rtc_ioctl NULL
#endif

static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
	return pcf8563_get_datetime(to_i2c_client(dev), tm);
}

static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
	return pcf8563_set_datetime(to_i2c_client(dev), tm);
}

static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
{
	struct i2c_client *client = to_i2c_client(dev);
	unsigned char buf[4];
	int err;

	err = pcf8563_read_block_data(client, PCF8563_REG_AMN, 4, buf);
	if (err)
		return err;

	dev_dbg(&client->dev,
		"%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n",
		__func__, buf[0], buf[1], buf[2], buf[3]);

	tm->time.tm_sec = 0;
	tm->time.tm_min = bcd2bin(buf[0] & 0x7F);
	tm->time.tm_hour = bcd2bin(buf[1] & 0x3F);
	tm->time.tm_mday = bcd2bin(buf[2] & 0x3F);
	tm->time.tm_wday = bcd2bin(buf[3] & 0x7);

	err = pcf8563_get_alarm_mode(client, &tm->enabled, &tm->pending);
	if (err < 0)
		return err;

	dev_dbg(&client->dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d,"
		" enabled=%d, pending=%d\n", __func__, tm->time.tm_min,
		tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday,
		tm->enabled, tm->pending);

	return 0;
}

static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
{
	struct i2c_client *client = to_i2c_client(dev);
	unsigned char buf[4];
	int err;

	/* The alarm has no seconds, round up to nearest minute */
	if (tm->time.tm_sec) {
		time64_t alarm_time = rtc_tm_to_time64(&tm->time);

		alarm_time += 60 - tm->time.tm_sec;
		rtc_time64_to_tm(alarm_time, &tm->time);
	}

	dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d "
		"enabled=%d pending=%d\n", __func__,
		tm->time.tm_min, tm->time.tm_hour, tm->time.tm_wday,
		tm->time.tm_mday, tm->enabled, tm->pending);

	buf[0] = bin2bcd(tm->time.tm_min);
	buf[1] = bin2bcd(tm->time.tm_hour);
	buf[2] = bin2bcd(tm->time.tm_mday);
	buf[3] = tm->time.tm_wday & 0x07;

	err = pcf8563_write_block_data(client, PCF8563_REG_AMN, 4, buf);
	if (err)
		return err;

	return pcf8563_set_alarm_mode(client, !!tm->enabled);
}

static int pcf8563_irq_enable(struct device *dev, unsigned int enabled)
{
	dev_dbg(dev, "%s: en=%d\n", __func__, enabled);
	return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
}

#ifdef CONFIG_COMMON_CLK
/*
 * Handling of the clkout
 */

#define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)

static int clkout_rates[] = {
	32768,
	1024,
	32,
	1,
};

static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw,
						unsigned long parent_rate)
{
	struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
	struct i2c_client *client = pcf8563->client;
	unsigned char buf;
	int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);

	if (ret < 0)
		return 0;

	buf &= PCF8563_REG_CLKO_F_MASK;
	return clkout_rates[buf];
}

static long pcf8563_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 pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
				   unsigned long parent_rate)
{
	struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
	struct i2c_client *client = pcf8563->client;
	unsigned char buf;
	int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
	int i;

	if (ret < 0)
		return ret;

	for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
		if (clkout_rates[i] == rate) {
			buf &= ~PCF8563_REG_CLKO_F_MASK;
			buf |= i;
			ret = pcf8563_write_block_data(client,
						       PCF8563_REG_CLKO, 1,
						       &buf);
			return ret;
		}

	return -EINVAL;
}

static int pcf8563_clkout_control(struct clk_hw *hw, bool enable)
{
	struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
	struct i2c_client *client = pcf8563->client;
	unsigned char buf;
	int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);

	if (ret < 0)
		return ret;

	if (enable)
		buf |= PCF8563_REG_CLKO_FE;
	else
		buf &= ~PCF8563_REG_CLKO_FE;

	ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
	return ret;
}

static int pcf8563_clkout_prepare(struct clk_hw *hw)
{
	return pcf8563_clkout_control(hw, 1);
}

static void pcf8563_clkout_unprepare(struct clk_hw *hw)
{
	pcf8563_clkout_control(hw, 0);
}

static int pcf8563_clkout_is_prepared(struct clk_hw *hw)
{
	struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
	struct i2c_client *client = pcf8563->client;
	unsigned char buf;
	int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);

	if (ret < 0)
		return ret;

	return !!(buf & PCF8563_REG_CLKO_FE);
}

static const struct clk_ops pcf8563_clkout_ops = {
	.prepare = pcf8563_clkout_prepare,
	.unprepare = pcf8563_clkout_unprepare,
	.is_prepared = pcf8563_clkout_is_prepared,
	.recalc_rate = pcf8563_clkout_recalc_rate,
	.round_rate = pcf8563_clkout_round_rate,
	.set_rate = pcf8563_clkout_set_rate,
};

static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
{
	struct i2c_client *client = pcf8563->client;
	struct device_node *node = client->dev.of_node;
	struct clk *clk;
	struct clk_init_data init;
	int ret;
	unsigned char buf;

	/* disable the clkout output */
	buf = 0;
	ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
	if (ret < 0)
		return ERR_PTR(ret);

	init.name = "pcf8563-clkout";
	init.ops = &pcf8563_clkout_ops;
	init.flags = 0;
	init.parent_names = NULL;
	init.num_parents = 0;
	pcf8563->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(&client->dev, &pcf8563->clkout_hw);

	if (!IS_ERR(clk))
		of_clk_add_provider(node, of_clk_src_simple_get, clk);

	return clk;
}
#endif

static const struct rtc_class_ops pcf8563_rtc_ops = {
	.ioctl		= pcf8563_rtc_ioctl,
	.read_time	= pcf8563_rtc_read_time,
	.set_time	= pcf8563_rtc_set_time,
	.read_alarm	= pcf8563_rtc_read_alarm,
	.set_alarm	= pcf8563_rtc_set_alarm,
	.alarm_irq_enable = pcf8563_irq_enable,
};

static int pcf8563_probe(struct i2c_client *client,
				const struct i2c_device_id *id)
{
	struct pcf8563 *pcf8563;
	int err;
	unsigned char buf;

	dev_dbg(&client->dev, "%s\n", __func__);

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
		return -ENODEV;

	pcf8563 = devm_kzalloc(&client->dev, sizeof(struct pcf8563),
				GFP_KERNEL);
	if (!pcf8563)
		return -ENOMEM;

	i2c_set_clientdata(client, pcf8563);
	pcf8563->client = client;
	device_set_wakeup_capable(&client->dev, 1);

	/* Set timer to lowest frequency to save power (ref Haoyu datasheet) */
	buf = PCF8563_TMRC_1_60;
	err = pcf8563_write_block_data(client, PCF8563_REG_TMRC, 1, &buf);
	if (err < 0) {
		dev_err(&client->dev, "%s: write error\n", __func__);
		return err;
	}

	/* Clear flags and disable interrupts */
	buf = 0;
	err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf);
	if (err < 0) {
		dev_err(&client->dev, "%s: write error\n", __func__);
		return err;
	}

	pcf8563->rtc = devm_rtc_device_register(&client->dev,
				pcf8563_driver.driver.name,
				&pcf8563_rtc_ops, THIS_MODULE);

	if (IS_ERR(pcf8563->rtc))
		return PTR_ERR(pcf8563->rtc);

	if (client->irq > 0) {
		err = devm_request_threaded_irq(&client->dev, client->irq,
				NULL, pcf8563_irq,
				IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW,
				pcf8563_driver.driver.name, client);
		if (err) {
			dev_err(&client->dev, "unable to request IRQ %d\n",
								client->irq);
			return err;
		}

	}

#ifdef CONFIG_COMMON_CLK
	/* register clk in common clk framework */
	pcf8563_clkout_register_clk(pcf8563);
#endif

	/* the pcf8563 alarm only supports a minute accuracy */
	pcf8563->rtc->uie_unsupported = 1;

	return 0;
}

static const struct i2c_device_id pcf8563_id[] = {
	{ "loongson,pcf8563", 0 },
	{ "rtc8564", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, pcf8563_id);

#ifdef CONFIG_OF
static const struct of_device_id pcf8563_of_match[] = {
	{ .compatible = "nxp,pcf8563" },
	{}
};
MODULE_DEVICE_TABLE(of, pcf8563_of_match);
#endif

static struct i2c_driver pcf8563_driver = {
	.driver		= {
		.name	= "rtc-pcf8563",
		.of_match_table = of_match_ptr(pcf8563_of_match),
	},
	.probe		= pcf8563_probe,
	.id_table	= pcf8563_id,
};

module_i2c_driver(pcf8563_driver);

MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver");
MODULE_LICENSE("GPL");

Makefile

obj-m += pcf8563_dev.o pcf8563_drv.o 
KDIR:=/home/asensing/loongson/linux-4.19
ARCH=loongarch 
CROSS_COMPILE=loongarch64-linux-gnu-
PWD?=$(shell pwd) 
all:
	make -C $(KDIR) M=$(PWD) modules 

实验操作

将pcf8563_dev.ko和pcf8563_drv.ko两个驱动模块部署到开发板,然后装载

insmod pcf8563_dev.ko
insmod pcf8563_drv.ko

可以看到/dev目录下又多了一个rtc节点

在这里插入图片描述

将系统时钟同步至RTC,因为龙芯的SOC自带RTC,所以要指定rtc的编号(rtc1)

hwclock -f /dev/rtc1 -w

查看rtc1时间
在这里插入图片描述

驱动分析

pcf8563_driver本质是一个i2c_driver,通过i2c_device_id和pcf8563_dev驱动匹配,rtc_class_ops需要实现几个函数接口

  • pcf8563_rtc_ioctl
  • pcf8563_rtc_read_time
  • pcf8563_rtc_set_time
  • pcf8563_rtc_read_alarm
  • pcf8563_rtc_set_alarm
  • pcf8563_irq_enable

参考

  • 参考自内核的RTC pcf8563驱动
  • PCF8563 Real-time clock/calendar (nxp.com)
  • 3.3. 使用 hwclock 命令 | Red Hat Product Documentation

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1982800.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一加Ace3 ColorOS14系统精简列表(免root)

#adb工具 https://www.123pan.com/s/Z3kujv-NHRPA.html 提取码:DZOD 使用&#xff1a; 1. 手机 设置 -- 关于本机 -- 版本信息 -- 版本号 &#xff0c;点5次 2. 设置 -- 其他设置 -- 开发者选项 -- USB调试 -- 无线调试 3.adb工具 -- adb connect 10.0.0.156:37909&#xff…

opencv-霍夫变换

霍夫变换就是一个可以让计算机学会自己找图形的算法。是图形处理领域内从图像中检测几何形状的基本方法之一。经典霍夫变换用来检测图像中的直线&#xff0c;后来霍夫变换经过扩展可以进行任意型状物体的识别&#xff0c;例如圆和椭圆。 霍夫变换运用两个坐标空间之间的变换&a…

CSP-J复赛 模拟题3

1.匿名信&#xff1a; 题目描述 出于对社会现状的担忧&#xff0c;Alice 决定给市长写一封建议信&#xff0c;考虑到市长的暴脾气&#xff0c;Alice 最终还是决定匿名上书&#xff0c;为了保证自己的绝对神秘&#xff0c;她还会从当天的报纸上将一些单词(或字母)裁剪下来并重…

安装glibc+mysql的权限问题

安装glibc glibc mysql 俗称绿色mysql 安装之前删掉mariadb: 数据库初始化时候&#xff0c;会⾃动找my.cnf配置&#xff0c;但是原有的mariadb配 置⽂件&#xff0c;会失败 [rootmysql3 ~]# ls -l /etc/my.cnf -rw-r--r--. 1 root root 570 6月 8 2017 /etc/my.cnf [rootm…

Vue路由入门学习

文章目录 路由的基本使用1.目标2.作用3.说明4.官网5.VueRouter的使用&#xff08;52&#xff09;两个核心步骤 组件的存放目录问题1.组件分类存放目录 路由的封装抽离 Vue中的路由&#xff1a; 路径和组件的 映射关系 路由的基本使用 1.目标 认识插件 VueRouter&#xff0c…

Midjourney仅10分钟搞定⼀套漫画,含MJ提⽰词分享

Midjourney能帮我们画漫画了,你敢信?⼀起来和⽹易设计师学习如何只⽤10分钟,即使你是设计⿇⽠,也能轻轻松松完成⼀张属于你⾃⼰的短⽚⼩漫画!今天就⽤MJ来实战⼀套漫画,包含创作⽤的⼯ 具和提⽰词的应⽤,以前来学习吧! 漫画先看成品图: ▍ Part1你要画个啥? 画漫画当…

复现一下最近学习的漏洞(sqlab 1-10)

第一个问题&#xff1a;为什么不能用#来闭合单引号呢&#xff1f; 在进行URL地址栏传参的时候&#xff0c;是有一套编码规范的。他不会编码英文、数字和某些符号。但是#它会进行编码。也就是%23。&#xff08;先转ascii码&#xff0c;然后再转十六进制&#xff0c;之后加上%就是…

小白暴力学习001---Vue---第一个Vue

做本文主要是采用Vue3的教程&#xff0c;来源于菜鸟教程&#xff0c; https://www.runoob.com/vue3/vue3-tutorial.html 主要记录从小白开始如何使用Vue建立一个笔记本的网站&#xff0c;并用于记录 基础条件&#xff1a; 有明确的目标有兴趣能使用电脑&#xff0c;会百度 视…

前端的学习-CSS(二)-弹性盒子-flex

一&#xff1a;子元素的属性 order&#xff1a;项目的排列顺序&#xff0c;数值越小&#xff0c;排列越靠前&#xff0c;默认为0。 flex-grow&#xff1a;定义项目的放大比例&#xff0c;默认为 0 &#xff0c;即如果存在剩余空间&#xff0c;也不放大。 flex-shrink&#xff1…

【链表OJ】常见面试题 2

文章目录 1.[链表分割](https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId8&&tqId11004&rp2&ru/activity/oj&qru/ta/cracking-the-coding-interview/question-ranking)1.1 题目要求1.2 哨兵位法 2.[链表的回文结构](https://www.…

Spring Boot集成liquibase快速入门Demo

1.什么是liquibase&#xff1f; Liquibase是一个用于跟踪、管理和应用数据库变化的开源的数据库重构工具。它将所有数据库的变化&#xff08;包括结构和数据&#xff09;都保存在 changelog 文件中&#xff0c;便于版本控制&#xff0c;它的目标是提供一种数据库类型无关的解决…

小厂也是厂,3000我也干

2018年6月&#xff0c;大三暑假 那一天&#xff0c;我投递了家里附近的一家公司有响应了&#xff0c;他线上问我什么时候可以去面试&#xff0c;我说什么时候都行。 HR&#xff1a;“要不你下午来吧&#xff1f;” 我&#xff1a;“行&#xff0c;我家里离面试地点不远” 我…

zdpy+vue3+onlyoffice文档系统实战上课笔记 20240805

上次 上次计划 1、最近文档表格完善 2、实现登录功能 3、新建文件&#xff0c;复制文件&#xff0c;删除文件 4、其他 目前任务&#xff1a;最近文档表格完善 1、在名称前面&#xff0c;渲染这个文档的图标 2、大小的基本的单位是kb&#xff0c;超过1024kb则换成mb&#xff0…

编写一个期货跨期套利的程序,谈谈思路及案例

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

8.5 C++

思维导图 试编程 提示并输入一个字符串&#xff0c;统计该字符中大写、小写字母个数、数字个数、空格个数以及其他字符个数 要求使用C风格字符串完成 #include <iostream> #include <array>using namespace std;int main() {cout << "请输入一个字符…

Python 字典 ({})的概念与操作

1、使用字典 在Python中&#xff0c;字典(dictionary)是一系列键值对(k-v pair)。每个键都有相应的值对应&#xff0c;使用键来访问与之关联的值&#xff0c;与键关联的值可以为数、字符串、列表乃至字典。 在Python中&#xff0c;字典放在花括号&#xff08;{}&#xff09;中…

C++中const关键字的用法

C语言和C中const的不同 首先我们需要区分一下C语言中的const和C中的const&#xff0c;C语言中的const修饰的变量可以不初始化&#xff0c;但如果将一个变量定位为const类型还不初始化&#xff0c;那么之后就不能对这个变量直接赋值了。 如果我们使用C语言中的const定义的变量指…

error: resource android:attr/lStar not found

项目打正式包的提示resource android:attr/lStar not found&#xff0c;后来分析发现android:attr/lStar属性在androidx.core:core的包里面&#xff0c;然后便试着降低androidx.core:core的版本号&#xff0c;降低后发现还是报错&#xff0c;查看错误信息发现androidx.core:cor…

安装nodejs服务器

Java项目可以运行在tomcat服务器&#xff0c;开始完成前后端完全分离。前端有自己独立的工程。我们需要把前端独立的工程运行起来。 运行在nodejs服务器下。 验证是否安装成功&#xff1a;敲cmd--输入node --version 1.安装npm java项目需要依赖jar,安装maven。前端项目也需要依…

《Advanced RAG》-07-探索 RAG 中表格数据的处理方案

摘要 本文详细讨论了实现 Retrieval-Augmented Generation&#xff08;RAG&#xff09;时对表格进行处理的挑战&#xff0c;特别是在非结构化文档中自动准确地提取和理解表格信息。 首先介绍了RAG中管理表格的关键技术&#xff0c;包括表格解析和索引结构设计。 接着&#xff0…