diff options
author | Lanqing Liu <lanqing.liu@unisoc.com> | 2019-03-04 03:58:22 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-03-27 11:30:00 -0400 |
commit | 4007098f4ce46c87c16ba34ddc00b042aa444b59 (patch) | |
tree | 061e53f3e9b33d5b2a7e6d827c23e68e26f80925 | |
parent | 984df54e4304d1ebe3e2320a9750b12236f14f0a (diff) |
serial: sprd: Add power management for the Spreadtrum serial controller
This patch adds power management for the Spreadtrum serial controller.
Signed-off-by: Lanqing Liu <lanqing.liu@unisoc.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/serial/sprd_serial.c | 61 |
1 files changed, 57 insertions, 4 deletions
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 1891a45ac05d..8f45b6671c19 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c | |||
@@ -100,10 +100,12 @@ | |||
100 | #define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) | 100 | #define SPRD_IMSR_TX_FIFO_EMPTY BIT(1) |
101 | #define SPRD_IMSR_BREAK_DETECT BIT(7) | 101 | #define SPRD_IMSR_BREAK_DETECT BIT(7) |
102 | #define SPRD_IMSR_TIMEOUT BIT(13) | 102 | #define SPRD_IMSR_TIMEOUT BIT(13) |
103 | #define SPRD_DEFAULT_SOURCE_CLK 26000000 | ||
103 | 104 | ||
104 | struct sprd_uart_port { | 105 | struct sprd_uart_port { |
105 | struct uart_port port; | 106 | struct uart_port port; |
106 | char name[16]; | 107 | char name[16]; |
108 | struct clk *clk; | ||
107 | }; | 109 | }; |
108 | 110 | ||
109 | static struct sprd_uart_port *sprd_port[UART_NR_MAX]; | 111 | static struct sprd_uart_port *sprd_port[UART_NR_MAX]; |
@@ -491,6 +493,22 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser) | |||
491 | return 0; | 493 | return 0; |
492 | } | 494 | } |
493 | 495 | ||
496 | static void sprd_pm(struct uart_port *port, unsigned int state, | ||
497 | unsigned int oldstate) | ||
498 | { | ||
499 | struct sprd_uart_port *sup = | ||
500 | container_of(port, struct sprd_uart_port, port); | ||
501 | |||
502 | switch (state) { | ||
503 | case UART_PM_STATE_ON: | ||
504 | clk_prepare_enable(sup->clk); | ||
505 | break; | ||
506 | case UART_PM_STATE_OFF: | ||
507 | clk_disable_unprepare(sup->clk); | ||
508 | break; | ||
509 | } | ||
510 | } | ||
511 | |||
494 | static const struct uart_ops serial_sprd_ops = { | 512 | static const struct uart_ops serial_sprd_ops = { |
495 | .tx_empty = sprd_tx_empty, | 513 | .tx_empty = sprd_tx_empty, |
496 | .get_mctrl = sprd_get_mctrl, | 514 | .get_mctrl = sprd_get_mctrl, |
@@ -507,6 +525,7 @@ static const struct uart_ops serial_sprd_ops = { | |||
507 | .request_port = sprd_request_port, | 525 | .request_port = sprd_request_port, |
508 | .config_port = sprd_config_port, | 526 | .config_port = sprd_config_port, |
509 | .verify_port = sprd_verify_port, | 527 | .verify_port = sprd_verify_port, |
528 | .pm = sprd_pm, | ||
510 | }; | 529 | }; |
511 | 530 | ||
512 | #ifdef CONFIG_SERIAL_SPRD_CONSOLE | 531 | #ifdef CONFIG_SERIAL_SPRD_CONSOLE |
@@ -671,11 +690,45 @@ static int sprd_remove(struct platform_device *dev) | |||
671 | return 0; | 690 | return 0; |
672 | } | 691 | } |
673 | 692 | ||
693 | static int sprd_clk_init(struct uart_port *uport) | ||
694 | { | ||
695 | struct clk *clk_uart, *clk_parent; | ||
696 | struct sprd_uart_port *u = sprd_port[uport->line]; | ||
697 | |||
698 | clk_uart = devm_clk_get(uport->dev, "uart"); | ||
699 | if (IS_ERR(clk_uart)) { | ||
700 | dev_warn(uport->dev, "uart%d can't get uart clock\n", | ||
701 | uport->line); | ||
702 | clk_uart = NULL; | ||
703 | } | ||
704 | |||
705 | clk_parent = devm_clk_get(uport->dev, "source"); | ||
706 | if (IS_ERR(clk_parent)) { | ||
707 | dev_warn(uport->dev, "uart%d can't get source clock\n", | ||
708 | uport->line); | ||
709 | clk_parent = NULL; | ||
710 | } | ||
711 | |||
712 | if (!clk_uart || clk_set_parent(clk_uart, clk_parent)) | ||
713 | uport->uartclk = SPRD_DEFAULT_SOURCE_CLK; | ||
714 | else | ||
715 | uport->uartclk = clk_get_rate(clk_uart); | ||
716 | |||
717 | u->clk = devm_clk_get(uport->dev, "enable"); | ||
718 | if (IS_ERR(u->clk)) { | ||
719 | if (PTR_ERR(u->clk) != -EPROBE_DEFER) | ||
720 | dev_err(uport->dev, "uart%d can't get enable clock\n", | ||
721 | uport->line); | ||
722 | return PTR_ERR(u->clk); | ||
723 | } | ||
724 | |||
725 | return 0; | ||
726 | } | ||
727 | |||
674 | static int sprd_probe(struct platform_device *pdev) | 728 | static int sprd_probe(struct platform_device *pdev) |
675 | { | 729 | { |
676 | struct resource *res; | 730 | struct resource *res; |
677 | struct uart_port *up; | 731 | struct uart_port *up; |
678 | struct clk *clk; | ||
679 | int irq; | 732 | int irq; |
680 | int index; | 733 | int index; |
681 | int ret; | 734 | int ret; |
@@ -704,9 +757,9 @@ static int sprd_probe(struct platform_device *pdev) | |||
704 | up->ops = &serial_sprd_ops; | 757 | up->ops = &serial_sprd_ops; |
705 | up->flags = UPF_BOOT_AUTOCONF; | 758 | up->flags = UPF_BOOT_AUTOCONF; |
706 | 759 | ||
707 | clk = devm_clk_get(&pdev->dev, NULL); | 760 | ret = sprd_clk_init(up); |
708 | if (!IS_ERR_OR_NULL(clk)) | 761 | if (ret) |
709 | up->uartclk = clk_get_rate(clk); | 762 | return ret; |
710 | 763 | ||
711 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 764 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
712 | up->membase = devm_ioremap_resource(&pdev->dev, res); | 765 | up->membase = devm_ioremap_resource(&pdev->dev, res); |