dm: treewide: Rename auto_alloc_size members to be shorter
[platform/kernel/u-boot.git] / arch / x86 / cpu / apollolake / uart.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Special driver to handle of-platdata
4  *
5  * Copyright 2019 Google LLC
6  *
7  * Some code from coreboot lpss.c
8  */
9
10 #include <common.h>
11 #include <dm.h>
12 #include <dt-structs.h>
13 #include <malloc.h>
14 #include <ns16550.h>
15 #include <spl.h>
16 #include <asm/io.h>
17 #include <asm/pci.h>
18 #include <asm/lpss.h>
19
20 /* Low-power Subsystem (LPSS) clock register */
21 enum {
22         LPSS_CLOCK_CTL_REG      = 0x200,
23         LPSS_CNT_CLOCK_EN       = 1,
24         LPSS_CNT_CLK_UPDATE     = 1U << 31,
25         LPSS_CLOCK_DIV_N_SHIFT  = 16,
26         LPSS_CLOCK_DIV_N_MASK   = 0x7fff << LPSS_CLOCK_DIV_N_SHIFT,
27         LPSS_CLOCK_DIV_M_SHIFT  = 1,
28         LPSS_CLOCK_DIV_M_MASK   = 0x7fff << LPSS_CLOCK_DIV_M_SHIFT,
29
30         /* These set the UART input clock speed */
31         LPSS_UART_CLK_M_VAL     = 0x25a,
32         LPSS_UART_CLK_N_VAL     = 0x7fff,
33 };
34
35 static void lpss_clk_update(void *regs, u32 clk_m_val, u32 clk_n_val)
36 {
37         u32 clk_sel;
38
39         clk_sel = clk_n_val << LPSS_CLOCK_DIV_N_SHIFT |
40                  clk_m_val << LPSS_CLOCK_DIV_M_SHIFT;
41         clk_sel |= LPSS_CNT_CLK_UPDATE | LPSS_CNT_CLOCK_EN;
42
43         writel(clk_sel, regs + LPSS_CLOCK_CTL_REG);
44 }
45
46 static void uart_lpss_init(void *regs)
47 {
48         /* Take UART out of reset */
49         lpss_reset_release(regs);
50
51         /* Set M and N divisor inputs and enable clock */
52         lpss_clk_update(regs, LPSS_UART_CLK_M_VAL, LPSS_UART_CLK_N_VAL);
53 }
54
55 void apl_uart_init(pci_dev_t bdf, ulong base)
56 {
57         /* Set UART base address */
58         pci_x86_write_config(bdf, PCI_BASE_ADDRESS_0, base, PCI_SIZE_32);
59
60         /* Enable memory access and bus master */
61         pci_x86_write_config(bdf, PCI_COMMAND, PCI_COMMAND_MEMORY |
62                              PCI_COMMAND_MASTER, PCI_SIZE_32);
63
64         uart_lpss_init((void *)base);
65 }
66
67 /*
68  * This driver uses its own compatible string but almost everything else from
69  * the standard ns16550 driver. This allows us to provide an of-platdata
70  * implementation, since the platdata produced by of-platdata does not match
71  * struct ns16550_platdata.
72  *
73  * When running with of-platdata (generally TPL), the platdata is converted to
74  * something that ns16550 expects. When running withoutof-platdata (SPL, U-Boot
75  * proper), we use ns16550's ofdata_to_platdata routine.
76  */
77
78 static int apl_ns16550_probe(struct udevice *dev)
79 {
80         struct ns16550_platdata *plat = dev_get_platdata(dev);
81
82         if (!CONFIG_IS_ENABLED(PCI))
83                 apl_uart_init(plat->bdf, plat->base);
84
85         return ns16550_serial_probe(dev);
86 }
87
88 static int apl_ns16550_ofdata_to_platdata(struct udevice *dev)
89 {
90 #if CONFIG_IS_ENABLED(OF_PLATDATA)
91         struct dtd_intel_apl_ns16550 *dtplat = dev_get_platdata(dev);
92         struct ns16550_platdata *plat;
93
94         /*
95          * Convert our platdata to the ns16550's platdata, so we can just use
96          * that driver
97          */
98         plat = malloc(sizeof(*plat));
99         if (!plat)
100                 return -ENOMEM;
101         plat->base = dtplat->early_regs[0];
102         plat->reg_width = 1;
103         plat->reg_shift = dtplat->reg_shift;
104         plat->reg_offset = 0;
105         plat->clock = dtplat->clock_frequency;
106         plat->fcr = UART_FCR_DEFVAL;
107         plat->bdf = pci_ofplat_get_devfn(dtplat->reg[0]);
108         dev->platdata = plat;
109 #else
110         int ret;
111
112         ret = ns16550_serial_ofdata_to_platdata(dev);
113         if (ret)
114                 return ret;
115 #endif /* OF_PLATDATA */
116
117         return 0;
118 }
119
120 static const struct udevice_id apl_ns16550_serial_ids[] = {
121         { .compatible = "intel,apl-ns16550" },
122         { },
123 };
124
125 U_BOOT_DRIVER(intel_apl_ns16550) = {
126         .name   = "intel_apl_ns16550",
127         .id     = UCLASS_SERIAL,
128         .of_match = apl_ns16550_serial_ids,
129         .platdata_auto  = sizeof(struct ns16550_platdata),
130         .priv_auto      = sizeof(struct NS16550),
131         .ops    = &ns16550_serial_ops,
132         .ofdata_to_platdata = apl_ns16550_ofdata_to_platdata,
133         .probe = apl_ns16550_probe,
134 };