Skip to content

Commit 86fa39d

Browse files
Praveen Talarigregkh
authored andcommitted
serial: qcom-geni: Enable Serial on SA8255p Qualcomm platforms
The Qualcomm automotive SA8255p SoC relies on firmware to configure platform resources, including clocks, interconnects and TLMM. The driver requests resources operations over SCMI using power and performance protocols. The SCMI power protocol enables or disables resources like clocks, interconnect paths, and TLMM (GPIOs) using runtime PM framework APIs, such as resume/suspend, to control power states(on/off). The SCMI performance protocol manages UART baud rates, with each baud rate represented by a performance level. The driver uses the dev_pm_opp_set_level() API to request the desired baud rate by specifying the performance level. Reviewed-by: Bryan O'Donoghue <[email protected]> Signed-off-by: Praveen Talari <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 1afa706 commit 86fa39d

File tree

1 file changed

+140
-16
lines changed

1 file changed

+140
-16
lines changed

drivers/tty/serial/qcom_geni_serial.c

Lines changed: 140 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/irq.h>
1212
#include <linux/module.h>
1313
#include <linux/of.h>
14+
#include <linux/pm_domain.h>
1415
#include <linux/pm_opp.h>
1516
#include <linux/platform_device.h>
1617
#include <linux/pm_runtime.h>
@@ -99,10 +100,16 @@
99100
#define DMA_RX_BUF_SIZE 2048
100101

101102
static DEFINE_IDA(port_ida);
103+
#define DOMAIN_IDX_POWER 0
104+
#define DOMAIN_IDX_PERF 1
102105

103106
struct qcom_geni_device_data {
104107
bool console;
105108
enum geni_se_xfer_mode mode;
109+
struct dev_pm_domain_attach_data pd_data;
110+
int (*resources_init)(struct uart_port *uport);
111+
int (*set_rate)(struct uart_port *uport, unsigned int baud);
112+
int (*power_state)(struct uart_port *uport, bool state);
106113
};
107114

108115
struct qcom_geni_private_data {
@@ -140,6 +147,7 @@ struct qcom_geni_serial_port {
140147

141148
struct qcom_geni_private_data private_data;
142149
const struct qcom_geni_device_data *dev_data;
150+
struct dev_pm_domain_list *pd_list;
143151
};
144152

145153
static const struct uart_ops qcom_geni_console_pops;
@@ -1362,6 +1370,42 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud)
13621370
return 0;
13631371
}
13641372

1373+
static int geni_serial_set_level(struct uart_port *uport, unsigned int baud)
1374+
{
1375+
struct qcom_geni_serial_port *port = to_dev_port(uport);
1376+
struct device *perf_dev = port->pd_list->pd_devs[DOMAIN_IDX_PERF];
1377+
1378+
/*
1379+
* The performance protocol sets UART communication
1380+
* speeds by selecting different performance levels
1381+
* through the OPP framework.
1382+
*
1383+
* Supported perf levels for baudrates in firmware are below
1384+
* +---------------------+--------------------+
1385+
* | Perf level value | Baudrate values |
1386+
* +---------------------+--------------------+
1387+
* | 300 | 300 |
1388+
* | 1200 | 1200 |
1389+
* | 2400 | 2400 |
1390+
* | 4800 | 4800 |
1391+
* | 9600 | 9600 |
1392+
* | 19200 | 19200 |
1393+
* | 38400 | 38400 |
1394+
* | 57600 | 57600 |
1395+
* | 115200 | 115200 |
1396+
* | 230400 | 230400 |
1397+
* | 460800 | 460800 |
1398+
* | 921600 | 921600 |
1399+
* | 2000000 | 2000000 |
1400+
* | 3000000 | 3000000 |
1401+
* | 3200000 | 3200000 |
1402+
* | 4000000 | 4000000 |
1403+
* +---------------------+--------------------+
1404+
*/
1405+
1406+
return dev_pm_opp_set_level(perf_dev, baud);
1407+
}
1408+
13651409
static void qcom_geni_serial_set_termios(struct uart_port *uport,
13661410
struct ktermios *termios,
13671411
const struct ktermios *old)
@@ -1380,7 +1424,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
13801424
/* baud rate */
13811425
baud = uart_get_baud_rate(uport, termios, old, 300, 8000000);
13821426

1383-
ret = geni_serial_set_rate(uport, baud);
1427+
ret = port->dev_data->set_rate(uport, baud);
13841428
if (ret)
13851429
return;
13861430

@@ -1667,8 +1711,27 @@ static int geni_serial_resources_off(struct uart_port *uport)
16671711
return 0;
16681712
}
16691713

1670-
static int geni_serial_resource_init(struct qcom_geni_serial_port *port)
1714+
static int geni_serial_resource_state(struct uart_port *uport, bool power_on)
1715+
{
1716+
return power_on ? geni_serial_resources_on(uport) : geni_serial_resources_off(uport);
1717+
}
1718+
1719+
static int geni_serial_pwr_init(struct uart_port *uport)
1720+
{
1721+
struct qcom_geni_serial_port *port = to_dev_port(uport);
1722+
int ret;
1723+
1724+
ret = dev_pm_domain_attach_list(port->se.dev,
1725+
&port->dev_data->pd_data, &port->pd_list);
1726+
if (ret <= 0)
1727+
return -EINVAL;
1728+
1729+
return 0;
1730+
}
1731+
1732+
static int geni_serial_resource_init(struct uart_port *uport)
16711733
{
1734+
struct qcom_geni_serial_port *port = to_dev_port(uport);
16721735
int ret;
16731736

16741737
port->se.clk = devm_clk_get(port->se.dev, "se");
@@ -1819,13 +1882,16 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
18191882
port->se.dev = &pdev->dev;
18201883
port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
18211884

1822-
ret = geni_serial_resource_init(port);
1885+
ret = port->dev_data->resources_init(uport);
18231886
if (ret)
18241887
return ret;
18251888

18261889
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1827-
if (!res)
1828-
return -EINVAL;
1890+
if (!res) {
1891+
ret = -EINVAL;
1892+
goto error;
1893+
}
1894+
18291895
uport->mapbase = res->start;
18301896

18311897
uport->rs485_config = qcom_geni_rs485_config;
@@ -1837,19 +1903,26 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
18371903
if (!data->console) {
18381904
port->rx_buf = devm_kzalloc(uport->dev,
18391905
DMA_RX_BUF_SIZE, GFP_KERNEL);
1840-
if (!port->rx_buf)
1841-
return -ENOMEM;
1906+
if (!port->rx_buf) {
1907+
ret = -ENOMEM;
1908+
goto error;
1909+
}
18421910
}
18431911

18441912
port->name = devm_kasprintf(uport->dev, GFP_KERNEL,
18451913
"qcom_geni_serial_%s%d",
18461914
uart_console(uport) ? "console" : "uart", uport->line);
1847-
if (!port->name)
1848-
return -ENOMEM;
1915+
if (!port->name) {
1916+
ret = -ENOMEM;
1917+
goto error;
1918+
}
18491919

18501920
irq = platform_get_irq(pdev, 0);
1851-
if (irq < 0)
1852-
return irq;
1921+
if (irq < 0) {
1922+
ret = irq;
1923+
goto error;
1924+
}
1925+
18531926
uport->irq = irq;
18541927
uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE);
18551928

@@ -1871,7 +1944,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
18711944
IRQF_TRIGGER_HIGH, port->name, uport);
18721945
if (ret) {
18731946
dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
1874-
return ret;
1947+
goto error;
18751948
}
18761949

18771950
ret = uart_get_rs485_mode(uport);
@@ -1882,7 +1955,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
18821955

18831956
ret = uart_add_one_port(drv, uport);
18841957
if (ret)
1885-
return ret;
1958+
goto error;
18861959

18871960
if (port->wakeup_irq > 0) {
18881961
device_init_wakeup(&pdev->dev, true);
@@ -1892,11 +1965,15 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
18921965
device_init_wakeup(&pdev->dev, false);
18931966
ida_free(&port_ida, uport->line);
18941967
uart_remove_one_port(drv, uport);
1895-
return ret;
1968+
goto error;
18961969
}
18971970
}
18981971

18991972
return 0;
1973+
1974+
error:
1975+
dev_pm_domain_detach_list(port->pd_list);
1976+
return ret;
19001977
}
19011978

19021979
static void qcom_geni_serial_remove(struct platform_device *pdev)
@@ -1909,22 +1986,31 @@ static void qcom_geni_serial_remove(struct platform_device *pdev)
19091986
device_init_wakeup(&pdev->dev, false);
19101987
ida_free(&port_ida, uport->line);
19111988
uart_remove_one_port(drv, &port->uport);
1989+
dev_pm_domain_detach_list(port->pd_list);
19121990
}
19131991

19141992
static int __maybe_unused qcom_geni_serial_runtime_suspend(struct device *dev)
19151993
{
19161994
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
19171995
struct uart_port *uport = &port->uport;
1996+
int ret = 0;
1997+
1998+
if (port->dev_data->power_state)
1999+
ret = port->dev_data->power_state(uport, false);
19182000

1919-
return geni_serial_resources_off(uport);
2001+
return ret;
19202002
}
19212003

19222004
static int __maybe_unused qcom_geni_serial_runtime_resume(struct device *dev)
19232005
{
19242006
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
19252007
struct uart_port *uport = &port->uport;
2008+
int ret = 0;
19262009

1927-
return geni_serial_resources_on(uport);
2010+
if (port->dev_data->power_state)
2011+
ret = port->dev_data->power_state(uport, true);
2012+
2013+
return ret;
19282014
}
19292015

19302016
static int qcom_geni_serial_suspend(struct device *dev)
@@ -1962,11 +2048,41 @@ static int qcom_geni_serial_resume(struct device *dev)
19622048
static const struct qcom_geni_device_data qcom_geni_console_data = {
19632049
.console = true,
19642050
.mode = GENI_SE_FIFO,
2051+
.resources_init = geni_serial_resource_init,
2052+
.set_rate = geni_serial_set_rate,
2053+
.power_state = geni_serial_resource_state,
19652054
};
19662055

19672056
static const struct qcom_geni_device_data qcom_geni_uart_data = {
19682057
.console = false,
19692058
.mode = GENI_SE_DMA,
2059+
.resources_init = geni_serial_resource_init,
2060+
.set_rate = geni_serial_set_rate,
2061+
.power_state = geni_serial_resource_state,
2062+
};
2063+
2064+
static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
2065+
.console = true,
2066+
.mode = GENI_SE_FIFO,
2067+
.pd_data = {
2068+
.pd_flags = PD_FLAG_DEV_LINK_ON,
2069+
.pd_names = (const char*[]) { "power", "perf" },
2070+
.num_pd_names = 2,
2071+
},
2072+
.resources_init = geni_serial_pwr_init,
2073+
.set_rate = geni_serial_set_level,
2074+
};
2075+
2076+
static const struct qcom_geni_device_data sa8255p_qcom_geni_uart_data = {
2077+
.console = false,
2078+
.mode = GENI_SE_DMA,
2079+
.pd_data = {
2080+
.pd_flags = PD_FLAG_DEV_LINK_ON,
2081+
.pd_names = (const char*[]) { "power", "perf" },
2082+
.num_pd_names = 2,
2083+
},
2084+
.resources_init = geni_serial_pwr_init,
2085+
.set_rate = geni_serial_set_level,
19702086
};
19712087

19722088
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
@@ -1980,10 +2096,18 @@ static const struct of_device_id qcom_geni_serial_match_table[] = {
19802096
.compatible = "qcom,geni-debug-uart",
19812097
.data = &qcom_geni_console_data,
19822098
},
2099+
{
2100+
.compatible = "qcom,sa8255p-geni-debug-uart",
2101+
.data = &sa8255p_qcom_geni_console_data,
2102+
},
19832103
{
19842104
.compatible = "qcom,geni-uart",
19852105
.data = &qcom_geni_uart_data,
19862106
},
2107+
{
2108+
.compatible = "qcom,sa8255p-geni-uart",
2109+
.data = &sa8255p_qcom_geni_uart_data,
2110+
},
19872111
{}
19882112
};
19892113
MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);

0 commit comments

Comments
 (0)