|
14 | 14 | * published by the Free Software Foundation. |
15 | 15 | */ |
16 | 16 |
|
| 17 | +#include <linux/clk-provider.h> |
17 | 18 | #include <linux/i2c.h> |
18 | 19 | #include <linux/bcd.h> |
19 | 20 | #include <linux/rtc.h> |
|
40 | 41 |
|
41 | 42 | #define PCF8563_REG_AMN 0x09 /* alarm */ |
42 | 43 |
|
43 | | -#define PCF8563_REG_CLKO 0x0D /* clock out */ |
| 44 | +#define PCF8563_REG_CLKO 0x0D /* clock out */ |
| 45 | +#define PCF8563_REG_CLKO_FE 0x80 /* clock out enabled */ |
| 46 | +#define PCF8563_REG_CLKO_F_MASK 0x03 /* frequenc mask */ |
| 47 | +#define PCF8563_REG_CLKO_F_32768HZ 0x00 |
| 48 | +#define PCF8563_REG_CLKO_F_1024HZ 0x01 |
| 49 | +#define PCF8563_REG_CLKO_F_32HZ 0x02 |
| 50 | +#define PCF8563_REG_CLKO_F_1HZ 0x03 |
| 51 | + |
44 | 52 | #define PCF8563_REG_TMRC 0x0E /* timer control */ |
45 | 53 | #define PCF8563_TMRC_ENABLE BIT(7) |
46 | 54 | #define PCF8563_TMRC_4096 0 |
@@ -76,6 +84,9 @@ struct pcf8563 { |
76 | 84 | int voltage_low; /* incicates if a low_voltage was detected */ |
77 | 85 |
|
78 | 86 | struct i2c_client *client; |
| 87 | +#ifdef CONFIG_COMMON_CLK |
| 88 | + struct clk_hw clkout_hw; |
| 89 | +#endif |
79 | 90 | }; |
80 | 91 |
|
81 | 92 | static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, |
@@ -390,6 +401,158 @@ static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) |
390 | 401 | return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled); |
391 | 402 | } |
392 | 403 |
|
| 404 | +#ifdef CONFIG_COMMON_CLK |
| 405 | +/* |
| 406 | + * Handling of the clkout |
| 407 | + */ |
| 408 | + |
| 409 | +#define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw) |
| 410 | + |
| 411 | +static int clkout_rates[] = { |
| 412 | + 32768, |
| 413 | + 1024, |
| 414 | + 32, |
| 415 | + 1, |
| 416 | +}; |
| 417 | + |
| 418 | +static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw, |
| 419 | + unsigned long parent_rate) |
| 420 | +{ |
| 421 | + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); |
| 422 | + struct i2c_client *client = pcf8563->client; |
| 423 | + unsigned char buf; |
| 424 | + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); |
| 425 | + |
| 426 | + if (ret < 0) |
| 427 | + return 0; |
| 428 | + |
| 429 | + buf &= PCF8563_REG_CLKO_F_MASK; |
| 430 | + return clkout_rates[ret]; |
| 431 | +} |
| 432 | + |
| 433 | +static long pcf8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate, |
| 434 | + unsigned long *prate) |
| 435 | +{ |
| 436 | + int i; |
| 437 | + |
| 438 | + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) |
| 439 | + if (clkout_rates[i] <= rate) |
| 440 | + return clkout_rates[i]; |
| 441 | + |
| 442 | + return 0; |
| 443 | +} |
| 444 | + |
| 445 | +static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, |
| 446 | + unsigned long parent_rate) |
| 447 | +{ |
| 448 | + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); |
| 449 | + struct i2c_client *client = pcf8563->client; |
| 450 | + unsigned char buf; |
| 451 | + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); |
| 452 | + int i; |
| 453 | + |
| 454 | + if (ret < 0) |
| 455 | + return ret; |
| 456 | + |
| 457 | + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) |
| 458 | + if (clkout_rates[i] == rate) { |
| 459 | + buf &= ~PCF8563_REG_CLKO_F_MASK; |
| 460 | + buf |= i; |
| 461 | + ret = pcf8563_write_block_data(client, |
| 462 | + PCF8563_REG_CLKO, 1, |
| 463 | + &buf); |
| 464 | + return ret; |
| 465 | + } |
| 466 | + |
| 467 | + return -EINVAL; |
| 468 | +} |
| 469 | + |
| 470 | +static int pcf8563_clkout_control(struct clk_hw *hw, bool enable) |
| 471 | +{ |
| 472 | + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); |
| 473 | + struct i2c_client *client = pcf8563->client; |
| 474 | + unsigned char buf; |
| 475 | + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); |
| 476 | + |
| 477 | + if (ret < 0) |
| 478 | + return ret; |
| 479 | + |
| 480 | + if (enable) |
| 481 | + buf |= PCF8563_REG_CLKO_FE; |
| 482 | + else |
| 483 | + buf &= ~PCF8563_REG_CLKO_FE; |
| 484 | + |
| 485 | + ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); |
| 486 | + return ret; |
| 487 | +} |
| 488 | + |
| 489 | +static int pcf8563_clkout_prepare(struct clk_hw *hw) |
| 490 | +{ |
| 491 | + return pcf8563_clkout_control(hw, 1); |
| 492 | +} |
| 493 | + |
| 494 | +static void pcf8563_clkout_unprepare(struct clk_hw *hw) |
| 495 | +{ |
| 496 | + pcf8563_clkout_control(hw, 0); |
| 497 | +} |
| 498 | + |
| 499 | +static int pcf8563_clkout_is_prepared(struct clk_hw *hw) |
| 500 | +{ |
| 501 | + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); |
| 502 | + struct i2c_client *client = pcf8563->client; |
| 503 | + unsigned char buf; |
| 504 | + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); |
| 505 | + |
| 506 | + if (ret < 0) |
| 507 | + return ret; |
| 508 | + |
| 509 | + return !!(buf & PCF8563_REG_CLKO_FE); |
| 510 | +} |
| 511 | + |
| 512 | +static const struct clk_ops pcf8563_clkout_ops = { |
| 513 | + .prepare = pcf8563_clkout_prepare, |
| 514 | + .unprepare = pcf8563_clkout_unprepare, |
| 515 | + .is_prepared = pcf8563_clkout_is_prepared, |
| 516 | + .recalc_rate = pcf8563_clkout_recalc_rate, |
| 517 | + .round_rate = pcf8563_clkout_round_rate, |
| 518 | + .set_rate = pcf8563_clkout_set_rate, |
| 519 | +}; |
| 520 | + |
| 521 | +static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563) |
| 522 | +{ |
| 523 | + struct i2c_client *client = pcf8563->client; |
| 524 | + struct device_node *node = client->dev.of_node; |
| 525 | + struct clk *clk; |
| 526 | + struct clk_init_data init; |
| 527 | + int ret; |
| 528 | + unsigned char buf; |
| 529 | + |
| 530 | + /* disable the clkout output */ |
| 531 | + buf = 0; |
| 532 | + ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); |
| 533 | + if (ret < 0) |
| 534 | + return ERR_PTR(ret); |
| 535 | + |
| 536 | + init.name = "pcf8563-clkout"; |
| 537 | + init.ops = &pcf8563_clkout_ops; |
| 538 | + init.flags = CLK_IS_ROOT; |
| 539 | + init.parent_names = NULL; |
| 540 | + init.num_parents = 0; |
| 541 | + pcf8563->clkout_hw.init = &init; |
| 542 | + |
| 543 | + /* optional override of the clockname */ |
| 544 | + of_property_read_string(node, "clock-output-names", &init.name); |
| 545 | + |
| 546 | + /* register the clock */ |
| 547 | + clk = devm_clk_register(&client->dev, &pcf8563->clkout_hw); |
| 548 | + |
| 549 | + if (!IS_ERR(clk)) |
| 550 | + of_clk_add_provider(node, of_clk_src_simple_get, clk); |
| 551 | + |
| 552 | + return clk; |
| 553 | +} |
| 554 | +#endif |
| 555 | + |
393 | 556 | static const struct rtc_class_ops pcf8563_rtc_ops = { |
394 | 557 | .ioctl = pcf8563_rtc_ioctl, |
395 | 558 | .read_time = pcf8563_rtc_read_time, |
@@ -459,6 +622,11 @@ static int pcf8563_probe(struct i2c_client *client, |
459 | 622 |
|
460 | 623 | } |
461 | 624 |
|
| 625 | +#ifdef CONFIG_COMMON_CLK |
| 626 | + /* register clk in common clk framework */ |
| 627 | + pcf8563_clkout_register_clk(pcf8563); |
| 628 | +#endif |
| 629 | + |
462 | 630 | /* the pcf8563 alarm only supports a minute accuracy */ |
463 | 631 | pcf8563->rtc->uie_unsupported = 1; |
464 | 632 |
|
|
0 commit comments