arm: tegra: dalmore/pluto: add imx091 nvc driver
[linux-3.10.git] / arch / arm / mach-tegra / board-dalmore-sensors.c
1 /*
2  * arch/arm/mach-tegra/board-dalmore-sensors.c
3  *
4  * Copyright (c) 2012 NVIDIA CORPORATION, All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  *
13  * Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * Neither the name of NVIDIA CORPORATION nor the names of its contributors
18  * may be used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  * This software is licensed under the terms of the GNU General Public
22  * License version 2, as published by the Free Software Foundation, and
23  * may be copied, distributed, and modified under those terms.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28  * GNU General Public License for more details.
29  */
30
31 #include <linux/i2c.h>
32 #include <linux/delay.h>
33 #include <linux/mpu.h>
34 #include <linux/regulator/consumer.h>
35 #include <linux/gpio.h>
36 #include <linux/therm_est.h>
37 #include <linux/nct1008.h>
38 #include <mach/edp.h>
39 #include <mach/gpio-tegra.h>
40 #include <mach/pinmux-t11.h>
41 #include <mach/pinmux.h>
42 #include <media/imx091.h>
43 #include <media/ov9772.h>
44 #include <media/as364x.h>
45 #include <media/ad5816.h>
46 #include <generated/mach-types.h>
47
48 #include "gpio-names.h"
49 #include "board.h"
50 #include "board-dalmore.h"
51 #include "cpu-tegra.h"
52 #include "devices.h"
53 #include "tegra-board-id.h"
54
55 static struct nvc_gpio_pdata imx091_gpio_pdata[] = {
56         {IMX091_GPIO_RESET, CAM_RSTN, true, false},
57         {IMX091_GPIO_PWDN, CAM1_POWER_DWN_GPIO, true, false},
58         {IMX091_GPIO_GP1, CAM_GPIO1, true, false}
59 };
60
61 static struct board_info board_info;
62
63 static struct balanced_throttle tj_throttle = {
64         .throt_tab_size = 10,
65         .throt_tab = {
66                 {      0, 1000 },
67                 { 640000, 1000 },
68                 { 640000, 1000 },
69                 { 640000, 1000 },
70                 { 640000, 1000 },
71                 { 640000, 1000 },
72                 { 760000, 1000 },
73                 { 760000, 1050 },
74                 {1000000, 1050 },
75                 {1000000, 1100 },
76         },
77 };
78
79 static struct nct1008_platform_data dalmore_nct1008_pdata = {
80         .supported_hwrev = true,
81         .ext_range = true,
82         .conv_rate = 0x08,
83         .offset = 80, /* 4 * 20C. Bug 844025 - 1C for device accuracies */
84         .shutdown_ext_limit = 90, /* C */
85         .shutdown_local_limit = 120, /* C */
86
87         /* Thermal Throttling */
88         .passive = {
89                 .create_cdev = (struct thermal_cooling_device *(*)(void *))
90                                 balanced_throttle_register,
91                 .cdev_data = &tj_throttle,
92                 .trip_temp = 80000,
93                 .tc1 = 0,
94                 .tc2 = 1,
95                 .passive_delay = 2000,
96         }
97 };
98
99 static struct i2c_board_info dalmore_i2c4_nct1008_board_info[] = {
100         {
101                 I2C_BOARD_INFO("nct1008", 0x4C),
102                 .platform_data = &dalmore_nct1008_pdata,
103                 .irq = -1,
104         }
105 };
106
107 #define VI_PINMUX(_pingroup, _mux, _pupd, _tri, _io, _lock, _ioreset) \
108         {                                                       \
109                 .pingroup       = TEGRA_PINGROUP_##_pingroup,   \
110                 .func           = TEGRA_MUX_##_mux,             \
111                 .pupd           = TEGRA_PUPD_##_pupd,           \
112                 .tristate       = TEGRA_TRI_##_tri,             \
113                 .io             = TEGRA_PIN_##_io,              \
114                 .lock           = TEGRA_PIN_LOCK_##_lock,       \
115                 .od             = TEGRA_PIN_OD_DEFAULT,         \
116                 .ioreset        = TEGRA_PIN_IO_RESET_##_ioreset \
117         }
118
119 static struct tegra_pingroup_config mclk_disable =
120         VI_PINMUX(CAM_MCLK, VI, NORMAL, NORMAL, OUTPUT, DEFAULT, DEFAULT);
121
122 static struct tegra_pingroup_config mclk_enable =
123         VI_PINMUX(CAM_MCLK, VI_ALT3, NORMAL, NORMAL, OUTPUT, DEFAULT, DEFAULT);
124
125 static struct tegra_pingroup_config pbb0_disable =
126         VI_PINMUX(GPIO_PBB0, VI, NORMAL, NORMAL, OUTPUT, DEFAULT, DEFAULT);
127
128 static struct tegra_pingroup_config pbb0_enable =
129         VI_PINMUX(GPIO_PBB0, VI_ALT3, NORMAL, NORMAL, OUTPUT, DEFAULT, DEFAULT);
130
131 /*
132  * As a workaround, dalmore_vcmvdd need to be allocated to activate the
133  * sensor devices. This is due to the focuser device(AD5816) will hook up
134  * the i2c bus if it is not powered up.
135 */
136 static struct regulator *dalmore_vcmvdd;
137
138 static int dalmore_get_vcmvdd(void)
139 {
140         if (!dalmore_vcmvdd) {
141                 dalmore_vcmvdd = regulator_get(NULL, "vdd_af_cam1");
142                 if (unlikely(WARN_ON(IS_ERR(dalmore_vcmvdd)))) {
143                         pr_err("%s: can't get regulator vcmvdd: %ld\n",
144                                 __func__, PTR_ERR(dalmore_vcmvdd));
145                         dalmore_vcmvdd = NULL;
146                         return -ENODEV;
147                 }
148         }
149         return 0;
150 }
151
152 static int dalmore_imx091_power_on(struct nvc_regulator *vreg)
153 {
154         int err;
155
156         if (unlikely(WARN_ON(!vreg)))
157                 return -EFAULT;
158
159         if (dalmore_get_vcmvdd())
160                 goto imx091_poweron_fail;
161
162         gpio_set_value(CAM1_POWER_DWN_GPIO, 0);
163         usleep_range(10, 20);
164
165         err = regulator_enable(vreg[IMX091_VREG_AVDD].vreg);
166         if (err)
167                 goto imx091_avdd_fail;
168
169         err = regulator_enable(vreg[IMX091_VREG_IOVDD].vreg);
170         if (err)
171                 goto imx091_iovdd_fail;
172
173         usleep_range(1, 2);
174         gpio_set_value(CAM1_POWER_DWN_GPIO, 1);
175
176         err = regulator_enable(dalmore_vcmvdd);
177         if (unlikely(err))
178                 goto imx091_vcmvdd_fail;
179
180         tegra_pinmux_config_table(&mclk_enable, 1);
181         usleep_range(300, 310);
182
183         return 1;
184
185 imx091_vcmvdd_fail:
186         regulator_disable(vreg[IMX091_VREG_IOVDD].vreg);
187
188 imx091_iovdd_fail:
189         regulator_disable(vreg[IMX091_VREG_AVDD].vreg);
190
191 imx091_avdd_fail:
192         gpio_set_value(CAM1_POWER_DWN_GPIO, 0);
193
194 imx091_poweron_fail:
195         pr_err("%s FAILED\n", __func__);
196         return -ENODEV;
197 }
198
199 static int dalmore_imx091_power_off(struct nvc_regulator *vreg)
200 {
201         if (unlikely(WARN_ON(!vreg)))
202                 return -EFAULT;
203
204         usleep_range(1, 2);
205         tegra_pinmux_config_table(&mclk_disable, 1);
206         gpio_set_value(CAM1_POWER_DWN_GPIO, 0);
207         usleep_range(1, 2);
208
209         regulator_disable(dalmore_vcmvdd);
210         regulator_disable(vreg[IMX091_VREG_IOVDD].vreg);
211         regulator_disable(vreg[IMX091_VREG_AVDD].vreg);
212
213         return 1;
214 }
215
216 static struct nvc_imager_cap imx091_cap = {
217         .identifier             = "IMX091",
218         .sensor_nvc_interface   = 3,
219         .pixel_types[0]         = 0x100,
220         .orientation            = 0,
221         .direction              = 0,
222         .initial_clock_rate_khz = 6000,
223         .clock_profiles[0] = {
224                 .external_clock_khz     = 24000,
225                 .clock_multiplier       = 10416667, /* value / 1,000,000 */
226         },
227         .clock_profiles[1] = {
228                 .external_clock_khz     = 0,
229                 .clock_multiplier       = 0,
230         },
231         .h_sync_edge            = 0,
232         .v_sync_edge            = 0,
233         .mclk_on_vgp0           = 0,
234         .csi_port               = 0,
235         .data_lanes             = 4,
236         .virtual_channel_id     = 0,
237         .discontinuous_clk_mode = 1,
238         .cil_threshold_settle   = 0x0,
239         .min_blank_time_width   = 16,
240         .min_blank_time_height  = 16,
241         .preferred_mode_index   = 0,
242         .focuser_guid           = NVC_FOCUS_GUID(0),
243         .torch_guid             = NVC_TORCH_GUID(0),
244         .cap_end                = NVC_IMAGER_CAPABILITIES_END,
245 };
246
247
248
249 static struct imx091_platform_data imx091_pdata = {
250         .num                    = 0,
251         .sync                   = 0,
252         .dev_name               = "camera",
253         .gpio_count             = ARRAY_SIZE(imx091_gpio_pdata),
254         .gpio                   = imx091_gpio_pdata,
255         .cap                    = &imx091_cap,
256         .power_on               = dalmore_imx091_power_on,
257         .power_off              = dalmore_imx091_power_off,
258 };
259
260 static int dalmore_ov9772_power_on(struct ov9772_power_rail *pw)
261 {
262         int err;
263
264         if (unlikely(!pw || !pw->avdd || !pw->dovdd))
265                 return -EFAULT;
266
267         if (dalmore_get_vcmvdd())
268                 goto ov9772_get_vcmvdd_fail;
269
270         gpio_set_value(CAM2_POWER_DWN_GPIO, 0);
271         gpio_set_value(CAM_RSTN, 0);
272
273         err = regulator_enable(pw->avdd);
274         if (unlikely(err))
275                 goto ov9772_avdd_fail;
276
277         err = regulator_enable(pw->dovdd);
278         if (unlikely(err))
279                 goto ov9772_dovdd_fail;
280
281         gpio_set_value(CAM_RSTN, 1);
282         gpio_set_value(CAM2_POWER_DWN_GPIO, 1);
283
284         err = regulator_enable(dalmore_vcmvdd);
285         if (unlikely(err))
286                 goto ov9772_vcmvdd_fail;
287
288         tegra_pinmux_config_table(&pbb0_enable, 1);
289         usleep_range(340, 380);
290
291         /* return 1 to skip the in-driver power_on sequence */
292         return 1;
293
294 ov9772_vcmvdd_fail:
295         regulator_disable(pw->dovdd);
296
297 ov9772_dovdd_fail:
298         regulator_disable(pw->avdd);
299
300 ov9772_avdd_fail:
301         gpio_set_value(CAM_RSTN, 0);
302         gpio_set_value(CAM2_POWER_DWN_GPIO, 0);
303
304 ov9772_get_vcmvdd_fail:
305         pr_err("%s FAILED\n", __func__);
306         return -ENODEV;
307 }
308
309 static int dalmore_ov9772_power_off(struct ov9772_power_rail *pw)
310 {
311         if (unlikely(!pw || !dalmore_vcmvdd || !pw->avdd || !pw->dovdd))
312                 return -EFAULT;
313
314         usleep_range(21, 25);
315         tegra_pinmux_config_table(&pbb0_disable, 1);
316
317         gpio_set_value(CAM2_POWER_DWN_GPIO, 0);
318         gpio_set_value(CAM_RSTN, 0);
319
320         regulator_disable(dalmore_vcmvdd);
321         regulator_disable(pw->dovdd);
322         regulator_disable(pw->avdd);
323
324         /* return 1 to skip the in-driver power_off sequence */
325         return 1;
326 }
327
328 static struct nvc_gpio_pdata ov9772_gpio_pdata[] = {
329         { OV9772_GPIO_TYPE_SHTDN, CAM2_POWER_DWN_GPIO, true, 0, },
330         { OV9772_GPIO_TYPE_PWRDN, CAM_RSTN, true, 0, },
331 };
332
333 static struct ov9772_platform_data dalmore_ov9772_pdata = {
334         .num            = 1,
335         .dev_name       = "camera",
336         .gpio_count     = ARRAY_SIZE(ov9772_gpio_pdata),
337         .gpio           = ov9772_gpio_pdata,
338         .power_on       = dalmore_ov9772_power_on,
339         .power_off      = dalmore_ov9772_power_off,
340 };
341
342 static int dalmore_as3648_power_on(struct as364x_power_rail *pw)
343 {
344         int err = dalmore_get_vcmvdd();
345
346         if (err)
347                 return err;
348
349         return regulator_enable(dalmore_vcmvdd);
350 }
351
352 static int dalmore_as3648_power_off(struct as364x_power_rail *pw)
353 {
354         if (!dalmore_vcmvdd)
355                 return -ENODEV;
356
357         return regulator_disable(dalmore_vcmvdd);
358 }
359
360 static struct as364x_platform_data dalmore_as3648_pdata = {
361         .config         = {
362                 .max_total_current_mA = 1000,
363                 .max_peak_current_mA = 600,
364                 .strobe_type = 1,
365                 },
366         .pinstate       = {
367                 .mask   = 1 << (CAM_FLASH_STROBE - TEGRA_GPIO_PBB0),
368                 .values = 1 << (CAM_FLASH_STROBE - TEGRA_GPIO_PBB0)
369                 },
370         .dev_name       = "torch",
371         .type           = AS3648,
372         .gpio_strobe    = CAM_FLASH_STROBE,
373         .led_mask       = 3,
374
375         .power_on_callback = dalmore_as3648_power_on,
376         .power_off_callback = dalmore_as3648_power_off,
377 };
378
379 static struct ad5816_platform_data pluto_ad5816_pdata = {
380         .cfg            = 0,
381         .num            = 0,
382         .sync           = 0,
383         .dev_name       = "focuser",
384 };
385
386 static struct i2c_board_info dalmore_i2c_board_info_e1625[] = {
387         {
388                 I2C_BOARD_INFO("imx091", 0x36),
389                 .platform_data = &imx091_pdata,
390         },
391         {
392                 I2C_BOARD_INFO("ov9772", 0x10),
393                 .platform_data = &dalmore_ov9772_pdata,
394         },
395         {
396                 I2C_BOARD_INFO("as3648", 0x30),
397                 .platform_data = &dalmore_as3648_pdata,
398         },
399         {
400                 I2C_BOARD_INFO("ad5816", 0x0E),
401                 .platform_data = &pluto_ad5816_pdata,
402         },
403 };
404
405 static int dalmore_camera_init(void)
406 {
407         tegra_pinmux_config_table(&mclk_disable, 1);
408         tegra_pinmux_config_table(&pbb0_disable, 1);
409
410         i2c_register_board_info(2, dalmore_i2c_board_info_e1625,
411                 ARRAY_SIZE(dalmore_i2c_board_info_e1625));
412         return 0;
413 }
414
415 /* MPU board file definition    */
416 static struct mpu_platform_data mpu9150_gyro_data = {
417         .int_config     = 0x10,
418         .level_shifter  = 0,
419         /* Located in board_[platformname].h */
420         .orientation    = MPU_GYRO_ORIENTATION,
421         .sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS,
422         .sec_slave_id   = COMPASS_ID_AK8975,
423         .secondary_i2c_addr     = MPU_COMPASS_ADDR,
424         .secondary_read_reg     = 0x06,
425         .secondary_orientation  = MPU_COMPASS_ORIENTATION,
426         .key            = {0x4E, 0xCC, 0x7E, 0xEB, 0xF6, 0x1E, 0x35, 0x22,
427                            0x00, 0x34, 0x0D, 0x65, 0x32, 0xE9, 0x94, 0x89},
428 };
429
430 #define TEGRA_CAMERA_GPIO(_gpio, _label, _value)                \
431         {                                                       \
432                 .gpio = _gpio,                                  \
433                 .label = _label,                                \
434                 .value = _value,                                \
435         }
436
437 static struct i2c_board_info dalmore_i2c_board_info_cm3218[] = {
438         {
439                 I2C_BOARD_INFO("cm3218", 0x48),
440         },
441 };
442
443 static struct i2c_board_info __initdata inv_mpu9150_i2c2_board_info[] = {
444         {
445                 I2C_BOARD_INFO(MPU_GYRO_NAME, MPU_GYRO_ADDR),
446                 .platform_data = &mpu9150_gyro_data,
447         },
448 };
449
450 static void mpuirq_init(void)
451 {
452         int ret = 0;
453         unsigned gyro_irq_gpio = MPU_GYRO_IRQ_GPIO;
454         unsigned gyro_bus_num = MPU_GYRO_BUS_NUM;
455         char *gyro_name = MPU_GYRO_NAME;
456
457         pr_info("*** MPU START *** mpuirq_init...\n");
458
459         ret = gpio_request(gyro_irq_gpio, gyro_name);
460
461         if (ret < 0) {
462                 pr_err("%s: gpio_request failed %d\n", __func__, ret);
463                 return;
464         }
465
466         ret = gpio_direction_input(gyro_irq_gpio);
467         if (ret < 0) {
468                 pr_err("%s: gpio_direction_input failed %d\n", __func__, ret);
469                 gpio_free(gyro_irq_gpio);
470                 return;
471         }
472         pr_info("*** MPU END *** mpuirq_init...\n");
473
474         inv_mpu9150_i2c2_board_info[0].irq = gpio_to_irq(MPU_GYRO_IRQ_GPIO);
475         i2c_register_board_info(gyro_bus_num, inv_mpu9150_i2c2_board_info,
476                 ARRAY_SIZE(inv_mpu9150_i2c2_board_info));
477 }
478
479 static int dalmore_nct1008_init(void)
480 {
481         int nct1008_port = -1;
482         int ret = 0;
483
484 #if defined(CONFIG_ARCH_TEGRA_11x_SOC)
485         if ((board_info.board_id == BOARD_E1611) ||
486             (board_info.board_id == BOARD_E1612) ||
487             (board_info.board_id == BOARD_E1641) ||
488             (board_info.board_id == BOARD_E1613) ||
489             (board_info.board_id == BOARD_P2454))
490         {
491                 /* per email from Matt 9/10/2012 */
492                 nct1008_port = TEGRA_GPIO_PX6;
493         } else {
494                 nct1008_port = TEGRA_GPIO_PX6;
495                 pr_err("Warning: nct alert_port assumed TEGRA_GPIO_PX6"
496                        " for unknown dalmore board id E%d\n",
497                        board_info.board_id);
498         }
499 #else
500         /* dalmore + AP30 interposer has SPI2_CS0 gpio */
501         nct1008_port = TEGRA_GPIO_PX3;
502 #endif
503
504         if (nct1008_port >= 0) {
505 #ifdef CONFIG_TEGRA_EDP_LIMITS
506                 const struct tegra_edp_limits *cpu_edp_limits;
507                 int cpu_edp_limits_size;
508                 int i;
509
510                 /* edp capping */
511                 tegra_get_cpu_edp_limits(&cpu_edp_limits, &cpu_edp_limits_size);
512
513                 if (cpu_edp_limits_size > MAX_THROT_TABLE_SIZE)
514                         BUG();
515
516                 for (i = 0; i < cpu_edp_limits_size-1; i++) {
517                         dalmore_nct1008_pdata.active[i].create_cdev =
518                                 (struct thermal_cooling_device *(*)(void *))
519                                         edp_cooling_device_create;
520                         dalmore_nct1008_pdata.active[i].cdev_data = (void *)i;
521                         dalmore_nct1008_pdata.active[i].trip_temp =
522                                 cpu_edp_limits[i].temperature * 1000;
523                         dalmore_nct1008_pdata.active[i].hysteresis = 1000;
524                 }
525                 dalmore_nct1008_pdata.active[i].create_cdev = NULL;
526 #endif
527
528                 dalmore_i2c4_nct1008_board_info[0].irq = gpio_to_irq(nct1008_port);
529                 pr_info("%s: dalmore nct1008 irq %d", __func__, dalmore_i2c4_nct1008_board_info[0].irq);
530
531                 ret = gpio_request(nct1008_port, "temp_alert");
532                 if (ret < 0)
533                         return ret;
534
535                 ret = gpio_direction_input(nct1008_port);
536                 if (ret < 0) {
537                         pr_info("%s: calling gpio_free(nct1008_port)", __func__);
538                         gpio_free(nct1008_port);
539                 }
540         }
541
542         /* dalmore has thermal sensor on GEN1-I2C i.e. instance 0 */
543         i2c_register_board_info(0, dalmore_i2c4_nct1008_board_info,
544                 ARRAY_SIZE(dalmore_i2c4_nct1008_board_info));
545
546         return ret;
547 }
548
549 static struct i2c_board_info __initdata bq20z45_pdata[] = {
550         {
551                 I2C_BOARD_INFO("sbs-battery", 0x0B),
552         },
553 };
554
555 #ifdef CONFIG_TEGRA_SKIN_THROTTLE
556 static int tegra_skin_match(struct thermal_zone_device *thz, void *data)
557 {
558         return strcmp((char *)data, thz->type) == 0;
559 }
560
561 static int tegra_skin_get_temp(void *data, long *temp)
562 {
563         struct thermal_zone_device *thz;
564
565         thz = thermal_zone_device_find(data, tegra_skin_match);
566
567         if (!thz || thz->ops->get_temp(thz, temp))
568                 *temp = 25000;
569
570         return 0;
571 }
572
573 static struct therm_est_data skin_data = {
574         .toffset = 9793,
575         .polling_period = 1100,
576         .ndevs = 2,
577         .devs = {
578                         {
579                                 .dev_data = "nct_ext",
580                                 .get_temp = tegra_skin_get_temp,
581                                 .coeffs = {
582                                         2, 1, 1, 1,
583                                         1, 1, 1, 1,
584                                         1, 1, 1, 0,
585                                         1, 1, 0, 0,
586                                         0, 0, -1, -7
587                                 },
588                         },
589                         {
590                                 .dev_data = "nct_int",
591                                 .get_temp = tegra_skin_get_temp,
592                                 .coeffs = {
593                                         -11, -7, -5, -3,
594                                         -3, -2, -1, 0,
595                                         0, 0, 1, 1,
596                                         1, 2, 2, 3,
597                                         4, 6, 11, 18
598                                 },
599                         },
600         },
601         .trip_temp = 43000,
602         .tc1 = 1,
603         .tc2 = 15,
604         .passive_delay = 15000,
605 };
606
607 static struct balanced_throttle skin_throttle = {
608         .throt_tab_size = 6,
609         .throt_tab = {
610                 { 640000, 1200 },
611                 { 640000, 1200 },
612                 { 760000, 1200 },
613                 { 760000, 1200 },
614                 {1000000, 1200 },
615                 {1000000, 1200 },
616         },
617 };
618
619 static int __init dalmore_skin_init(void)
620 {
621         struct thermal_cooling_device *skin_cdev;
622
623         skin_cdev = balanced_throttle_register(&skin_throttle);
624
625         skin_data.cdev = skin_cdev;
626         tegra_skin_therm_est_device.dev.platform_data = &skin_data;
627         platform_device_register(&tegra_skin_therm_est_device);
628
629         return 0;
630 }
631 late_initcall(dalmore_skin_init);
632 #endif
633
634 int __init dalmore_sensors_init(void)
635 {
636         int err;
637
638         tegra_get_board_info(&board_info);
639
640         err = dalmore_nct1008_init();
641         if (err)
642                 return err;
643
644         dalmore_camera_init();
645         mpuirq_init();
646
647         i2c_register_board_info(0, dalmore_i2c_board_info_cm3218,
648                 ARRAY_SIZE(dalmore_i2c_board_info_cm3218));
649
650         i2c_register_board_info(0, bq20z45_pdata,
651                 ARRAY_SIZE(bq20z45_pdata));
652
653         return 0;
654 }