Skip to content

Commit abffd1e

Browse files
ptalari27gregkh
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. Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com> Link: https://patch.msgid.link/20251110101043.2108414-5-praveen.talari@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 10904d7 commit abffd1e

1 file changed

Lines changed: 141 additions & 17 deletions

File tree

drivers/tty/serial/qcom_geni_serial.c

Lines changed: 141 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/irq.h>
1515
#include <linux/module.h>
1616
#include <linux/of.h>
17+
#include <linux/pm_domain.h>
1718
#include <linux/pm_opp.h>
1819
#include <linux/platform_device.h>
1920
#include <linux/pm_runtime.h>
@@ -101,10 +102,16 @@
101102
#define DMA_RX_BUF_SIZE 2048
102103

103104
static DEFINE_IDA(port_ida);
105+
#define DOMAIN_IDX_POWER 0
106+
#define DOMAIN_IDX_PERF 1
104107

105108
struct qcom_geni_device_data {
106109
bool console;
107110
enum geni_se_xfer_mode mode;
111+
struct dev_pm_domain_attach_data pd_data;
112+
int (*resources_init)(struct uart_port *uport);
113+
int (*set_rate)(struct uart_port *uport, unsigned int baud);
114+
int (*power_state)(struct uart_port *uport, bool state);
108115
};
109116

110117
struct qcom_geni_private_data {
@@ -142,6 +149,7 @@ struct qcom_geni_serial_port {
142149

143150
struct qcom_geni_private_data private_data;
144151
const struct qcom_geni_device_data *dev_data;
152+
struct dev_pm_domain_list *pd_list;
145153
};
146154

147155
static const struct uart_ops qcom_geni_console_pops;
@@ -1299,6 +1307,42 @@ static int geni_serial_set_rate(struct uart_port *uport, unsigned int baud)
12991307
return 0;
13001308
}
13011309

1310+
static int geni_serial_set_level(struct uart_port *uport, unsigned int baud)
1311+
{
1312+
struct qcom_geni_serial_port *port = to_dev_port(uport);
1313+
struct device *perf_dev = port->pd_list->pd_devs[DOMAIN_IDX_PERF];
1314+
1315+
/*
1316+
* The performance protocol sets UART communication
1317+
* speeds by selecting different performance levels
1318+
* through the OPP framework.
1319+
*
1320+
* Supported perf levels for baudrates in firmware are below
1321+
* +---------------------+--------------------+
1322+
* | Perf level value | Baudrate values |
1323+
* +---------------------+--------------------+
1324+
* | 300 | 300 |
1325+
* | 1200 | 1200 |
1326+
* | 2400 | 2400 |
1327+
* | 4800 | 4800 |
1328+
* | 9600 | 9600 |
1329+
* | 19200 | 19200 |
1330+
* | 38400 | 38400 |
1331+
* | 57600 | 57600 |
1332+
* | 115200 | 115200 |
1333+
* | 230400 | 230400 |
1334+
* | 460800 | 460800 |
1335+
* | 921600 | 921600 |
1336+
* | 2000000 | 2000000 |
1337+
* | 3000000 | 3000000 |
1338+
* | 3200000 | 3200000 |
1339+
* | 4000000 | 4000000 |
1340+
* +---------------------+--------------------+
1341+
*/
1342+
1343+
return dev_pm_opp_set_level(perf_dev, baud);
1344+
}
1345+
13021346
static void qcom_geni_serial_set_termios(struct uart_port *uport,
13031347
struct ktermios *termios,
13041348
const struct ktermios *old)
@@ -1317,7 +1361,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
13171361
/* baud rate */
13181362
baud = uart_get_baud_rate(uport, termios, old, 300, 8000000);
13191363

1320-
ret = geni_serial_set_rate(uport, baud);
1364+
ret = port->dev_data->set_rate(uport, baud);
13211365
if (ret)
13221366
return;
13231367

@@ -1604,8 +1648,27 @@ static int geni_serial_resources_off(struct uart_port *uport)
16041648
return 0;
16051649
}
16061650

1607-
static int geni_serial_resource_init(struct qcom_geni_serial_port *port)
1651+
static int geni_serial_resource_state(struct uart_port *uport, bool power_on)
1652+
{
1653+
return power_on ? geni_serial_resources_on(uport) : geni_serial_resources_off(uport);
1654+
}
1655+
1656+
static int geni_serial_pwr_init(struct uart_port *uport)
1657+
{
1658+
struct qcom_geni_serial_port *port = to_dev_port(uport);
1659+
int ret;
1660+
1661+
ret = dev_pm_domain_attach_list(port->se.dev,
1662+
&port->dev_data->pd_data, &port->pd_list);
1663+
if (ret <= 0)
1664+
return -EINVAL;
1665+
1666+
return 0;
1667+
}
1668+
1669+
static int geni_serial_resource_init(struct uart_port *uport)
16081670
{
1671+
struct qcom_geni_serial_port *port = to_dev_port(uport);
16091672
int ret;
16101673

16111674
port->se.clk = devm_clk_get(port->se.dev, "se");
@@ -1756,13 +1819,16 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
17561819
port->se.dev = &pdev->dev;
17571820
port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
17581821

1759-
ret = geni_serial_resource_init(port);
1822+
ret = port->dev_data->resources_init(uport);
17601823
if (ret)
17611824
return ret;
17621825

17631826
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1764-
if (!res)
1765-
return -EINVAL;
1827+
if (!res) {
1828+
ret = -EINVAL;
1829+
goto error;
1830+
}
1831+
17661832
uport->mapbase = res->start;
17671833

17681834
uport->rs485_config = qcom_geni_rs485_config;
@@ -1774,19 +1840,26 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
17741840
if (!data->console) {
17751841
port->rx_buf = devm_kzalloc(uport->dev,
17761842
DMA_RX_BUF_SIZE, GFP_KERNEL);
1777-
if (!port->rx_buf)
1778-
return -ENOMEM;
1843+
if (!port->rx_buf) {
1844+
ret = -ENOMEM;
1845+
goto error;
1846+
}
17791847
}
17801848

17811849
port->name = devm_kasprintf(uport->dev, GFP_KERNEL,
17821850
"qcom_geni_serial_%s%d",
17831851
uart_console(uport) ? "console" : "uart", uport->line);
1784-
if (!port->name)
1785-
return -ENOMEM;
1852+
if (!port->name) {
1853+
ret = -ENOMEM;
1854+
goto error;
1855+
}
17861856

17871857
irq = platform_get_irq(pdev, 0);
1788-
if (irq < 0)
1789-
return irq;
1858+
if (irq < 0) {
1859+
ret = irq;
1860+
goto error;
1861+
}
1862+
17901863
uport->irq = irq;
17911864
uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE);
17921865

@@ -1808,18 +1881,18 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
18081881
IRQF_TRIGGER_HIGH, port->name, uport);
18091882
if (ret) {
18101883
dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
1811-
return ret;
1884+
goto error;
18121885
}
18131886

18141887
ret = uart_get_rs485_mode(uport);
18151888
if (ret)
1816-
return ret;
1889+
goto error;
18171890

18181891
devm_pm_runtime_enable(port->se.dev);
18191892

18201893
ret = uart_add_one_port(drv, uport);
18211894
if (ret)
1822-
return ret;
1895+
goto error;
18231896

18241897
if (port->wakeup_irq > 0) {
18251898
device_init_wakeup(&pdev->dev, true);
@@ -1829,11 +1902,15 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
18291902
device_init_wakeup(&pdev->dev, false);
18301903
ida_free(&port_ida, uport->line);
18311904
uart_remove_one_port(drv, uport);
1832-
return ret;
1905+
goto error;
18331906
}
18341907
}
18351908

18361909
return 0;
1910+
1911+
error:
1912+
dev_pm_domain_detach_list(port->pd_list);
1913+
return ret;
18371914
}
18381915

18391916
static void qcom_geni_serial_remove(struct platform_device *pdev)
@@ -1846,22 +1923,31 @@ static void qcom_geni_serial_remove(struct platform_device *pdev)
18461923
device_init_wakeup(&pdev->dev, false);
18471924
ida_free(&port_ida, uport->line);
18481925
uart_remove_one_port(drv, &port->uport);
1926+
dev_pm_domain_detach_list(port->pd_list);
18491927
}
18501928

18511929
static int __maybe_unused qcom_geni_serial_runtime_suspend(struct device *dev)
18521930
{
18531931
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
18541932
struct uart_port *uport = &port->uport;
1933+
int ret = 0;
18551934

1856-
return geni_serial_resources_off(uport);
1935+
if (port->dev_data->power_state)
1936+
ret = port->dev_data->power_state(uport, false);
1937+
1938+
return ret;
18571939
}
18581940

18591941
static int __maybe_unused qcom_geni_serial_runtime_resume(struct device *dev)
18601942
{
18611943
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
18621944
struct uart_port *uport = &port->uport;
1945+
int ret = 0;
1946+
1947+
if (port->dev_data->power_state)
1948+
ret = port->dev_data->power_state(uport, true);
18631949

1864-
return geni_serial_resources_on(uport);
1950+
return ret;
18651951
}
18661952

18671953
static int qcom_geni_serial_suspend(struct device *dev)
@@ -1899,11 +1985,41 @@ static int qcom_geni_serial_resume(struct device *dev)
18991985
static const struct qcom_geni_device_data qcom_geni_console_data = {
19001986
.console = true,
19011987
.mode = GENI_SE_FIFO,
1988+
.resources_init = geni_serial_resource_init,
1989+
.set_rate = geni_serial_set_rate,
1990+
.power_state = geni_serial_resource_state,
19021991
};
19031992

19041993
static const struct qcom_geni_device_data qcom_geni_uart_data = {
19051994
.console = false,
19061995
.mode = GENI_SE_DMA,
1996+
.resources_init = geni_serial_resource_init,
1997+
.set_rate = geni_serial_set_rate,
1998+
.power_state = geni_serial_resource_state,
1999+
};
2000+
2001+
static const struct qcom_geni_device_data sa8255p_qcom_geni_console_data = {
2002+
.console = true,
2003+
.mode = GENI_SE_FIFO,
2004+
.pd_data = {
2005+
.pd_flags = PD_FLAG_DEV_LINK_ON,
2006+
.pd_names = (const char*[]) { "power", "perf" },
2007+
.num_pd_names = 2,
2008+
},
2009+
.resources_init = geni_serial_pwr_init,
2010+
.set_rate = geni_serial_set_level,
2011+
};
2012+
2013+
static const struct qcom_geni_device_data sa8255p_qcom_geni_uart_data = {
2014+
.console = false,
2015+
.mode = GENI_SE_DMA,
2016+
.pd_data = {
2017+
.pd_flags = PD_FLAG_DEV_LINK_ON,
2018+
.pd_names = (const char*[]) { "power", "perf" },
2019+
.num_pd_names = 2,
2020+
},
2021+
.resources_init = geni_serial_pwr_init,
2022+
.set_rate = geni_serial_set_level,
19072023
};
19082024

19092025
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
@@ -1917,10 +2033,18 @@ static const struct of_device_id qcom_geni_serial_match_table[] = {
19172033
.compatible = "qcom,geni-debug-uart",
19182034
.data = &qcom_geni_console_data,
19192035
},
2036+
{
2037+
.compatible = "qcom,sa8255p-geni-debug-uart",
2038+
.data = &sa8255p_qcom_geni_console_data,
2039+
},
19202040
{
19212041
.compatible = "qcom,geni-uart",
19222042
.data = &qcom_geni_uart_data,
19232043
},
2044+
{
2045+
.compatible = "qcom,sa8255p-geni-uart",
2046+
.data = &sa8255p_qcom_geni_uart_data,
2047+
},
19242048
{}
19252049
};
19262050
MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);

0 commit comments

Comments
 (0)