For the work of learning to drive, pay attention to the first use, and then understand. OK, let's see how the board is registered?
In the board file, its registration function is this:
IMX6Q_ADD_IMX_SNVS_RTC ()
Okay, let's track it down:
1 extern const struct Imx_snvs_rtc_data imx6q_imx_snvs_rtc_data __initconst; 2 #define IMX6Q_ADD_IMX_SNVS_RTC () 3 IMX_ADD_SNVS_RTC (&imx6q_imx_snvs_rtc_data) 4 5 #define Imx_ Snvs_rtc_data_entry_single (Soc) 6 { 7 . Iobase = Soc # # _SNVS_BASE_ADDR, 8 . IRQ = Soc # # _int_ Snvs, 9 }10 #ifdef config_soc_imx6q12 const struct Imx_snvs_rtc_data imx6q_imx_snvs_rtc_data __initconst = Imx_snvs_rtc_data_entry_single (mx6q); #endif/* ifdef config_soc_imx6q */15-struct Platform_device *__init IMX_ADD_SNVS_RTC (the const struct Imx_snvs_rtc_data *data) { resource res[] = { . Start = data->iobase,22 . end = Data->iobase + sz_4k-1,23 . Flags = ioresource_mem,24 }, {. s Tart = data->irq,26 . end = data->irq,27 . Flags = ioresource_irq,28 },29 };30 return Imx_add_platform_device ("SNVS_RTC", 0,32 Res, array_size (res), NULL, 0); 33}
Finally call Imx_add_platform_device to register the RTC in.
So what is the code like on the driver side? The main parts of the analysis are:
1/*! 2 * The RTC driver structure 3 */4 static struct Rtc_class_ops Snvs_rtc_ops = {5 . open = Snvs_rtc_open, 6
.release = snvs_rtc_release, 7 . Read_time = Snvs_rtc_read_time, 8 . set_time = snvs_rtc_set_time, 9 . Read_ Alarm = snvs_rtc_read_alarm,10 . set_alarm = snvs_rtc_set_alarm,11 . proc = snvs_rtc_proc,12 . IOCTL = Snvs_ rtc_ioctl,13 . alarm_irq_enable = snvs_rtc_alarm_irq_enable,14};
The function instance inside the Rtc_class_ops needs to be done.
Define a PLATFORM_DRIVER structure:
1/*! 2 * Contains pointers to the power management callback functions. 3 */ 4 static struct Platform_driver snvs_rtc_dr Iver = {5 . Driver = {6 . Name = "SNVS_RTC", //Note this should be the same as the device side name 7 }, 8 . Probe = Snvs_rtc_probe, 9
.remove = __exit_p (snvs_rtc_remove), suspend = snvs_rtc_suspend,11 . Resume = snvs_rtc_resume,12};
Complete the registration of the Rtc_class_ops in the probe function. Well, the detailed analysis under probe:
1/*! Snvs RTC Power Management control */2 static int snvs_rtc_probe (struct platform_device *pdev) 3 {4 struct Timespec t V 5 struct resource *res; 6 struct Rtc_device *RTC; 7 struct Rtc_drv_data *pdata = NULL; 8 void __iomem *ioaddr; 9 u32 lp_cr;10 INT ret = 0;11 res = Platform_get_resource (Pdev, Ioresource_mem, 0); Acquisition of resources, that is, the first address of memory registered in the device (!res) return-enodev;15 pdata = Kzalloc (sizeof (*pdata), Gfp_kernel); if (!pdata) return-enomem;19 pdata->baseaddr = res->start;21 pdata->ioaddr = Ioremap (Pdata->baseaddr, 0XC00); Allocate IO memory space ioaddr = pdata->ioaddr;23 Pdata->irq = PLATFORM_GET_IRQ (Pdev, 0); Get Interrupt number Platform_set_drvdata (Pdev, pdata); Private data assigned to Platform_device Rtc_drv_data */Added to support Sysfs wakealarm attribute */28 Pdev->dev.pow Er.can_wakeup = 1; */* Initialize glitch DetecT///After allocating IO memory, it is possible to perform some initialization of the hardware for its registers. __raw_writel (Snvs_lppgdr_init, ioaddr + SNVS_LPPGDR); Udelay (+); * * Clear LP Interrupt status */35 __raw_writel (0xFFFFFFFF, ioaddr + snvs_lpsr), PNS/* Enable RTC */38 LP_CR = __raw_readl (ioaddr + SNVS_LPCR) ; if (LP_CR & snvs_lpcr_srtc_env) = = 0) __raw_writel (LP_CR | Snvs_lpcr_srtc_env, ioaddr + SNVS_LPCR); Udelay (0xFFFFFFFF, __raw_writel + ioaddr); 45 Udelay (PDATA->IRQ >= 0) {//Set interrupt function if (REQUEST_IRQ (PDATA->IRQ, Snvs_rtc_interrupt, irqf_shared,49 pdev->name, Pdev) < 0) {Dev_warn , "Interrupt not available.\n"); PDATA->IRQ = -1;52} else {DISABLE_IRQ (pdata-> IRQ); pdata->irq_enable = false;55}56}57, RTC = Rtc_device_register (Pdev->name, &A Mp;pdev->Dev,//important!!! RTC Device Registration!!! &snvs_rtc_ops, This_module), if (Is_err (RTC)) {Got ret = Ptr_err (RTC); o err_out;63}64 PDATA->RTC = RTC; Assign the RTC on the registration to the driver! Tv.tv_nsec = 0;68 tv.tv_sec = rtc_read_lp_counter (ioaddr + SNVS_LPSRTCMR);/* REMOVE can_wakeup FLA G to add common power wakeup interface */71 pdev->dev.power.can_wakeup = 0;72/* By default, devices should Wakeup if they can */74/* So Snvs is set as "should wakeup" as it can */75 Device_init_wakeup (&pdev->dev, 1); ret;78 err_out:80 Iounmap (ioaddr); Bayi if (pdata->irq >= 0) in the FREE_IRQ (PDATA-&G T;IRQ, Pdev); Kfree (pdata); return ret;85}
OK, with some comments on it, the basic process is to get the device's resources, mem or IRQ, and then map the memory space to initialize the hardware. Register the IRQ and set the IRQ service function.
Then register the RTC device and register the Rtc_class_ops with the device. Assign the RTC device to the RTC driver.
Remove function
1 static int __exit snvs_rtc_remove (struct platform_device *pdev) 2 {3 struct rtc_drv_data *pdata = PLATFORM_GET_DRVD ATA (Pdev); 4 Rtc_device_unregister (PDATA->RTC); 5 if (PDATA->IRQ >= 0) 6 free_irq (PDATA->IRQ, Pdev); 7 8 Kfree (pdata); 9 return 0;10}
Get the drive data inside the device and then log the drive inside the RTC. Unregisters an IRQ. Frees the drive data space.
The Init function and exit function inside the drive are simple, registering and unregistering for Platform_driver.
1/*! 2 * Contains pointers to the power management callback functions. 3 */4 static struct Platform_driver Snvs_ Rtc_driver = {5. Driver = {6. Name = "SNVS_RTC", 7}, 8. Probe = Snvs_rtc_probe, 9. Re move = __exit_p (snvs_rtc_remove), suspend = snvs_rtc_suspend,11. Resume = snvs_rtc_resume,12};13 +/*!15 * T His function creates THE/PROC/DRIVER/RTC file and registers the device RTC16 * in The/dev/misc directory. It also reads the RTC value from external SOURCE17 * and setup the internal RTC properly.18 *19 * @return-1 if RTC is Failed to initialize; 0 is successful.20 */21 static int __init snvs_rtc_init (void) (Platform_driver_register ver);}25/*!27 * This function removes THE/PROC/DRIVER/RTC file and un-registers The28 * device RTC from The/dev /misc directory.29 */30 static void __exit snvs_rtc_exit (void) + Platform_driver_unregister (&snvs_rtc_driver) ;
Well, this is Imx6q's own RTC, it has a disadvantage is the large power flow!!! So Freescale engineers also do not recommend using!!! Button battery won't last long!
We chose a Intersil company's isl1208 as the RTC chip. Its driver, which is available on the Linux release, is added to the config file. How to realize the function of RTC???
Or first look at the device side, because isl1208 is isl1208 is the I²c interface, so we only need to register its I²C information on the board level, this information includes isl1208 address, as well as the driver name. Both of these messages should be noted!
Its address: 1101111X, when x for 0 o'clock is a write operation, for 1 is read operation. In the I2c_board_info registration is not the back of the X-position, high-right shift one bit. Its address is 0x6f;
Driver Name: Device driver name is consistent with name inside i2c_device_id, not i2c_driver driver name.
Well, look at the device-side settings:
1 static struct Imxi2c_platform_data Mx6q_sabresd_i2c_data = {2 . Bitrate = 100000, 3}; 4 5 static struct I2C_BOA Rd_info mxc_i2c0_board_info[] __initdata = {6 {7 i2c_board_info ("wm89**", 0x1a), 8 }, 9 {ten I2c_board_ INFO ("Ov564x", 0x3c), one by one . Platform_data = (void *) &camera_data,12 },13 { i2c_board_info (" mma8451 ", 0x1c), platform_data = (void *) &mma8451_position,16 },17 { i2c_board_info (" isl1208 ", 0x6f), },20 21};
Inside the Board_init function, add:
1 imx6q_add_imx_i2c (0, &mx6q_sabresd_i2c_data);
2 i2c_register_board_info (0, Mxc_i2c0_board_info, array_size (Mxc_i2c0_board_info));
This allows the device terminal to complete the registration work.
What about the driver side?
Similar to the above, to complete a rtc_device_register will isl1208_rtc_ops these operating hardware functions to register in.
The release of the code is available, it is not posted.
Luo Li Bar So much, in fact, is to mention some of the code of the process. It's not enough to understand the code! Try to do it!
IMX6Q RTC Drive Analysis