From 1543e317f1da31b75942316931e8f491a8920811 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Thu, 04 Jan 2024 10:08:02 +0000
Subject: [PATCH] disable FB
---
kernel/drivers/tty/serial/sc16is7xx.c | 190 +++++++++++++++++++++++++++++-----------------
1 files changed, 119 insertions(+), 71 deletions(-)
diff --git a/kernel/drivers/tty/serial/sc16is7xx.c b/kernel/drivers/tty/serial/sc16is7xx.c
index ebea4a9..c681ed5 100644
--- a/kernel/drivers/tty/serial/sc16is7xx.c
+++ b/kernel/drivers/tty/serial/sc16is7xx.c
@@ -14,9 +14,9 @@
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
@@ -315,6 +315,7 @@
struct kthread_work tx_work;
struct kthread_work reg_work;
struct sc16is7xx_one_config config;
+ bool irda_mode;
};
struct sc16is7xx_port {
@@ -327,9 +328,8 @@
unsigned char buf[SC16IS7XX_FIFO_SIZE];
struct kthread_worker kworker;
struct task_struct *kworker_task;
- struct kthread_work irq_work;
struct mutex efr_lock;
- struct sc16is7xx_one p[0];
+ struct sc16is7xx_one p[];
};
static unsigned long sc16is7xx_lines;
@@ -710,9 +710,9 @@
return true;
}
-static void sc16is7xx_ist(struct kthread_work *ws)
+static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
{
- struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work);
+ struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
mutex_lock(&s->efr_lock);
@@ -727,13 +727,6 @@
}
mutex_unlock(&s->efr_lock);
-}
-
-static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
-{
- struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
-
- kthread_queue_work(&s->kworker, &s->irq_work);
return IRQ_HANDLED;
}
@@ -741,12 +734,15 @@
static void sc16is7xx_tx_proc(struct kthread_work *ws)
{
struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port);
+ struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
if ((port->rs485.flags & SER_RS485_ENABLED) &&
(port->rs485.delay_rts_before_send > 0))
msleep(port->rs485.delay_rts_before_send);
+ mutex_lock(&s->efr_lock);
sc16is7xx_handle_tx(port);
+ mutex_unlock(&s->efr_lock);
}
static void sc16is7xx_reconf_rs485(struct uart_port *port)
@@ -994,6 +990,7 @@
static int sc16is7xx_startup(struct uart_port *port)
{
+ struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
unsigned int val;
@@ -1031,6 +1028,13 @@
/* Now, initialize the UART */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8);
+
+ /* Enable IrDA mode if requested in DT */
+ /* This bit must be written with LCR[7] = 0 */
+ sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
+ SC16IS7XX_MCR_IRDA_BIT,
+ one->irda_mode ?
+ SC16IS7XX_MCR_IRDA_BIT : 0);
/* Enable the Rx and Tx FIFO */
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
@@ -1166,9 +1170,18 @@
state |= BIT(offset);
else
state &= ~BIT(offset);
- sc16is7xx_port_write(port, SC16IS7XX_IOSTATE_REG, state);
+
+ /*
+ * If we write IOSTATE first, and then IODIR, the output value is not
+ * transferred to the corresponding I/O pin.
+ * The datasheet states that each register bit will be transferred to
+ * the corresponding I/O pin programmed as output when writing to
+ * IOSTATE. Therefore, configure direction first with IODIR, and then
+ * set value after with IOSTATE.
+ */
sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset),
BIT(offset));
+ sc16is7xx_port_write(port, SC16IS7XX_IOSTATE_REG, state);
return 0;
}
@@ -1176,29 +1189,45 @@
static int sc16is7xx_probe(struct device *dev,
const struct sc16is7xx_devtype *devtype,
- struct regmap *regmap, int irq, unsigned long flags)
+ struct regmap *regmap, int irq)
{
- struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 };
- unsigned long freq, *pfreq = dev_get_platdata(dev);
+ unsigned long freq = 0, *pfreq = dev_get_platdata(dev);
+ unsigned int val;
+ u32 uartclk = 0;
int i, ret;
struct sc16is7xx_port *s;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
+ /*
+ * This device does not have an identification register that would
+ * tell us if we are really connected to the correct device.
+ * The best we can do is to check if communication is at all possible.
+ */
+ ret = regmap_read(regmap,
+ SC16IS7XX_LSR_REG << SC16IS7XX_REG_SHIFT, &val);
+ if (ret < 0)
+ return -EPROBE_DEFER;
+
/* Alloc port structure */
- s = devm_kzalloc(dev, sizeof(*s) +
- sizeof(struct sc16is7xx_one) * devtype->nr_uart,
- GFP_KERNEL);
+ s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL);
if (!s) {
dev_err(dev, "Error allocating port structure\n");
return -ENOMEM;
}
+ /* Always ask for fixed clock rate from a property. */
+ device_property_read_u32(dev, "clock-frequency", &uartclk);
+
s->clk = devm_clk_get(dev, NULL);
if (IS_ERR(s->clk)) {
+ if (uartclk)
+ freq = uartclk;
if (pfreq)
freq = *pfreq;
+ if (freq)
+ dev_dbg(dev, "Clock frequency: %luHz\n", freq);
else
return PTR_ERR(s->clk);
} else {
@@ -1215,33 +1244,13 @@
mutex_init(&s->efr_lock);
kthread_init_worker(&s->kworker);
- kthread_init_work(&s->irq_work, sc16is7xx_ist);
s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker,
"sc16is7xx");
if (IS_ERR(s->kworker_task)) {
ret = PTR_ERR(s->kworker_task);
goto out_clk;
}
- sched_setscheduler(s->kworker_task, SCHED_FIFO, &sched_param);
-
-#ifdef CONFIG_GPIOLIB
- if (devtype->nr_gpio) {
- /* Setup GPIO cotroller */
- s->gpio.owner = THIS_MODULE;
- s->gpio.parent = dev;
- s->gpio.label = dev_name(dev);
- s->gpio.direction_input = sc16is7xx_gpio_direction_input;
- s->gpio.get = sc16is7xx_gpio_get;
- s->gpio.direction_output = sc16is7xx_gpio_direction_output;
- s->gpio.set = sc16is7xx_gpio_set;
- s->gpio.base = -1;
- s->gpio.ngpio = devtype->nr_gpio;
- s->gpio.can_sleep = 1;
- ret = gpiochip_add_data(&s->gpio, s);
- if (ret)
- goto out_thread;
- }
-#endif
+ sched_set_fifo(s->kworker_task);
/* reset device, purging any pending irq / data */
regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT,
@@ -1255,6 +1264,13 @@
s->p[i].port.type = PORT_SC16IS7XX;
s->p[i].port.fifosize = SC16IS7XX_FIFO_SIZE;
s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY;
+ s->p[i].port.iobase = i;
+ /*
+ * Use all ones as membase to make sure uart_configure_port() in
+ * serial_core.c does not abort for SPI/I2C devices where the
+ * membase address is not applicable.
+ */
+ s->p[i].port.membase = (void __iomem *)~0;
s->p[i].port.iotype = UPIO_PORT;
s->p[i].port.uartclk = freq;
s->p[i].port.rs485_config = sc16is7xx_config_rs485;
@@ -1296,17 +1312,54 @@
sc16is7xx_power(&s->p[i].port, 0);
}
- /* Setup interrupt */
- ret = devm_request_irq(dev, irq, sc16is7xx_irq,
- flags, dev_name(dev), s);
+ if (dev->of_node) {
+ struct property *prop;
+ const __be32 *p;
+ u32 u;
+
+ of_property_for_each_u32(dev->of_node, "irda-mode-ports",
+ prop, p, u)
+ if (u < devtype->nr_uart)
+ s->p[u].irda_mode = true;
+ }
+
+#ifdef CONFIG_GPIOLIB
+ if (devtype->nr_gpio) {
+ /* Setup GPIO cotroller */
+ s->gpio.owner = THIS_MODULE;
+ s->gpio.parent = dev;
+ s->gpio.label = dev_name(dev);
+ s->gpio.direction_input = sc16is7xx_gpio_direction_input;
+ s->gpio.get = sc16is7xx_gpio_get;
+ s->gpio.direction_output = sc16is7xx_gpio_direction_output;
+ s->gpio.set = sc16is7xx_gpio_set;
+ s->gpio.base = -1;
+ s->gpio.ngpio = devtype->nr_gpio;
+ s->gpio.can_sleep = 1;
+ ret = gpiochip_add_data(&s->gpio, s);
+ if (ret)
+ goto out_thread;
+ }
+#endif
+
+ /*
+ * Setup interrupt. We first try to acquire the IRQ line as level IRQ.
+ * If that succeeds, we can allow sharing the interrupt as well.
+ * In case the interrupt controller doesn't support that, we fall
+ * back to a non-shared falling-edge trigger.
+ */
+ ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_irq,
+ IRQF_TRIGGER_LOW | IRQF_SHARED |
+ IRQF_ONESHOT,
+ dev_name(dev), s);
if (!ret)
return 0;
-out_ports:
- for (i--; i >= 0; i--) {
- uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
- clear_bit(s->p[i].port.line, &sc16is7xx_lines);
- }
+ ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(dev), s);
+ if (!ret)
+ return 0;
#ifdef CONFIG_GPIOLIB
if (devtype->nr_gpio)
@@ -1314,6 +1367,13 @@
out_thread:
#endif
+
+out_ports:
+ for (i--; i >= 0; i--) {
+ uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
+ clear_bit(s->p[i].port.line, &sc16is7xx_lines);
+ }
+
kthread_stop(s->kworker_task);
out_clk:
@@ -1372,7 +1432,6 @@
static int sc16is7xx_spi_probe(struct spi_device *spi)
{
const struct sc16is7xx_devtype *devtype;
- unsigned long flags = 0;
struct regmap *regmap;
int ret;
@@ -1386,25 +1445,20 @@
return ret;
if (spi->dev.of_node) {
- const struct of_device_id *of_id =
- of_match_device(sc16is7xx_dt_ids, &spi->dev);
-
- if (!of_id)
+ devtype = device_get_match_data(&spi->dev);
+ if (!devtype)
return -ENODEV;
-
- devtype = (struct sc16is7xx_devtype *)of_id->data;
} else {
const struct spi_device_id *id_entry = spi_get_device_id(spi);
devtype = (struct sc16is7xx_devtype *)id_entry->driver_data;
- flags = IRQF_TRIGGER_FALLING;
}
regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) |
(devtype->nr_uart - 1);
regmap = devm_regmap_init_spi(spi, ®cfg);
- return sc16is7xx_probe(&spi->dev, devtype, regmap, spi->irq, flags);
+ return sc16is7xx_probe(&spi->dev, devtype, regmap, spi->irq);
}
static int sc16is7xx_spi_remove(struct spi_device *spi)
@@ -1428,7 +1482,7 @@
static struct spi_driver sc16is7xx_spi_uart_driver = {
.driver = {
.name = SC16IS7XX_NAME,
- .of_match_table = of_match_ptr(sc16is7xx_dt_ids),
+ .of_match_table = sc16is7xx_dt_ids,
},
.probe = sc16is7xx_spi_probe,
.remove = sc16is7xx_spi_remove,
@@ -1443,27 +1497,21 @@
const struct i2c_device_id *id)
{
const struct sc16is7xx_devtype *devtype;
- unsigned long flags = 0;
struct regmap *regmap;
if (i2c->dev.of_node) {
- const struct of_device_id *of_id =
- of_match_device(sc16is7xx_dt_ids, &i2c->dev);
-
- if (!of_id)
+ devtype = device_get_match_data(&i2c->dev);
+ if (!devtype)
return -ENODEV;
-
- devtype = (struct sc16is7xx_devtype *)of_id->data;
} else {
devtype = (struct sc16is7xx_devtype *)id->driver_data;
- flags = IRQF_TRIGGER_FALLING;
}
regcfg.max_register = (0xf << SC16IS7XX_REG_SHIFT) |
(devtype->nr_uart - 1);
regmap = devm_regmap_init_i2c(i2c, ®cfg);
- return sc16is7xx_probe(&i2c->dev, devtype, regmap, i2c->irq, flags);
+ return sc16is7xx_probe(&i2c->dev, devtype, regmap, i2c->irq);
}
static int sc16is7xx_i2c_remove(struct i2c_client *client)
@@ -1486,7 +1534,7 @@
static struct i2c_driver sc16is7xx_i2c_uart_driver = {
.driver = {
.name = SC16IS7XX_NAME,
- .of_match_table = of_match_ptr(sc16is7xx_dt_ids),
+ .of_match_table = sc16is7xx_dt_ids,
},
.probe = sc16is7xx_i2c_probe,
.remove = sc16is7xx_i2c_remove,
@@ -1524,11 +1572,11 @@
#ifdef CONFIG_SERIAL_SC16IS7XX_SPI
err_spi:
+#endif
#ifdef CONFIG_SERIAL_SC16IS7XX_I2C
i2c_del_driver(&sc16is7xx_i2c_uart_driver);
-#endif
-#endif
err_i2c:
+#endif
uart_unregister_driver(&sc16is7xx_uart);
return ret;
}
--
Gitblit v1.6.2