Message ID | 20211127223253.19098-1-semen.protsenko@linaro.org |
---|---|
Headers | show |
Series | soc: samsung: Add USIv2 driver | expand |
On Sun, 28 Nov 2021 at 00:33, Sam Protsenko <semen.protsenko@linaro.org> wrote: > > When HSI2C is encapsulated in USIv2 block (e.g. in Exynos850), USIv2 > driver must be loaded first, as it's preparing USI hardware for > particular protocol use. Make it impossible for i2c-exynos5 driver to be > built-in when USIv2 driver is built as a module, to prevent incorrect > booting order for those drivers. > > Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> > --- This patch is not needed, please ignore it. > drivers/i2c/busses/Kconfig | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig > index df89cb809330..e815a9dffb2c 100644 > --- a/drivers/i2c/busses/Kconfig > +++ b/drivers/i2c/busses/Kconfig > @@ -613,6 +613,7 @@ config I2C_EXYNOS5 > tristate "Exynos high-speed I2C driver" > depends on OF > depends on ARCH_EXYNOS || COMPILE_TEST > + depends on EXYNOS_USI_V2 || !EXYNOS_USI_V2 > default y if ARCH_EXYNOS > help > High-speed I2C controller on Samsung Exynos5 and newer Samsung SoCs: > -- > 2.30.2 >
On 27/11/2021 23:32, Sam Protsenko wrote: > USIv2 IP-core is found on modern ARM64 Exynos SoCs (like Exynos850) and > provides selectable serial protocol (one of: UART, SPI, I2C). USIv2 > registers usually reside in the same register map as a particular > underlying protocol it implements, but have some particular offset. E.g. > on Exynos850 the USI_UART has 0x13820000 base address, where UART > registers have 0x00..0x40 offsets, and USI registers have 0xc0..0xdc > offsets. Desired protocol can be chosen via SW_CONF register from System > Register block of the same domain as USI. > > Before starting to use a particular protocol, USIv2 must be configured > properly: > 1. Select protocol to be used via System Register > 2. Clear "reset" flag in USI_CON > 3. Configure HWACG behavior (e.g. for UART Rx the HWACG must be > disabled, so that the IP clock is not gated automatically); this is > done using USI_OPTION register > 4. Keep both USI clocks (PCLK and IPCLK) running during USI registers > modification > > This driver implements above behavior. Of course, USIv2 driver should be > probed before UART/I2C/SPI drivers. It can be achived by embedding > UART/I2C/SPI nodes inside of USI node (in Device Tree); driver then > walks underlying nodes and instantiates those. Driver also handles USI > configuration on PM resume, as register contents can be lost during CPU > suspend. > > Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> > --- > drivers/soc/samsung/Kconfig | 14 ++ > drivers/soc/samsung/Makefile | 2 + > drivers/soc/samsung/exynos-usi-v2.c | 242 ++++++++++++++++++++++++++++ You used everywhere v2 naming, but I actually hope this driver will be able to support also v1 and vx of USI. IOW, I expect to have only one USI driver, so please drop everywhere v2 (bindings, symbols, Kconfig, functions) except the compatible. > 3 files changed, 258 insertions(+) > create mode 100644 drivers/soc/samsung/exynos-usi-v2.c > > diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig > index e2cedef1e8d1..b168973c887f 100644 > --- a/drivers/soc/samsung/Kconfig > +++ b/drivers/soc/samsung/Kconfig > @@ -23,6 +23,20 @@ config EXYNOS_CHIPID > Support for Samsung Exynos SoC ChipID and Adaptive Supply Voltage. > This driver can also be built as module (exynos_chipid). > > +config EXYNOS_USI_V2 > + tristate "Exynos USIv2 (Universal Serial Interface) driver" > + default ARCH_EXYNOS && ARM64 > + depends on ARCH_EXYNOS || COMPILE_TEST > + select MFD_SYSCON > + help > + Enable support for USIv2 block. USI (Universal Serial Interface) is an > + IP-core found in modern Samsung Exynos SoCs, like Exynos850 and > + ExynosAutoV0. USI block can be configured to provide one of the > + following serial protocols: UART, SPI or High Speed I2C. > + > + This driver allows one to configure USI for desired protocol, which > + is usually done in USI node in Device Tree. > + > config EXYNOS_PMU > bool "Exynos PMU controller driver" if COMPILE_TEST > depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST) > diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile > index 2ae4bea804cf..0b746b2fd78f 100644 > --- a/drivers/soc/samsung/Makefile > +++ b/drivers/soc/samsung/Makefile > @@ -4,6 +4,8 @@ obj-$(CONFIG_EXYNOS_ASV_ARM) += exynos5422-asv.o > obj-$(CONFIG_EXYNOS_CHIPID) += exynos_chipid.o > exynos_chipid-y += exynos-chipid.o exynos-asv.o > > +obj-$(CONFIG_EXYNOS_USI_V2) += exynos-usi-v2.o > + > obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o > > obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS) += exynos3250-pmu.o exynos4-pmu.o \ > diff --git a/drivers/soc/samsung/exynos-usi-v2.c b/drivers/soc/samsung/exynos-usi-v2.c > new file mode 100644 > index 000000000000..5a315890e4ec > --- /dev/null > +++ b/drivers/soc/samsung/exynos-usi-v2.c > @@ -0,0 +1,242 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2021 Linaro Ltd. > + * Author: Sam Protsenko <semen.protsenko@linaro.org> > + * > + * Samsung Exynos USI v2 driver (Universal Serial Interface). > + */ > + > +#include <linux/clk.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/of_platform.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > +#include <linux/mfd/syscon.h> > + > +#include <dt-bindings/soc/samsung,exynos-usi-v2.h> > + > +/* System Register: SW_CONF register bits */ > +#define SW_CONF_UART BIT(0) > +#define SW_CONF_SPI BIT(1) > +#define SW_CONF_I2C BIT(2) > +#define SW_CONF_MASK (SW_CONF_UART | SW_CONF_SPI | SW_CONF_I2C) > + > +/* USI register offsets */ > +#define USI_CON 0x04 > +#define USI_OPTION 0x08 > + > +/* USI register bits */ > +#define USI_CON_RESET BIT(0) > +#define USI_OPTION_CLKREQ_ON BIT(1) > +#define USI_OPTION_CLKSTOP_ON BIT(2) > + > +struct usi_v2_mode { Everywhere here: s/usi_v2/exynos_usi/ > + const char *name; /* mode name */ > + unsigned int val; /* mode register value */ > +}; > + > +struct usi_v2 { > + struct device *dev;> + void __iomem *regs; /* USI register map */ > + struct clk *pclk; /* USI bus clock */ > + struct clk *ipclk; /* USI operating clock */ > + > + size_t mode; /* current USI SW_CONF mode index */ > + bool clkreq_on; /* always provide clock to IP */ > + > + /* System Register */ > + struct regmap *sysreg; /* System Register map */ > + unsigned int sw_conf; /* SW_CONF register offset in sysreg */ > +}; > + > +static const struct usi_v2_mode usi_v2_modes[] = { > + [USI_V2_UART] = { .name = "uart", .val = SW_CONF_UART }, > + [USI_V2_SPI] = { .name = "spi", .val = SW_CONF_SPI }, > + [USI_V2_I2C] = { .name = "i2c", .val = SW_CONF_I2C }, > +}; > + > +/** > + * usi_v2_set_sw_conf - Set USI block configuration mode > + * @usi: USI driver object > + * @mode: Mode index > + * > + * Select underlying serial protocol (UART/SPI/I2C) in USI IP-core. > + * > + * Return: 0 on success, or negative error code on failure. > + */ > +static int usi_v2_set_sw_conf(struct usi_v2 *usi, size_t mode) > +{ > + unsigned int val; > + int ret; > + > + if (mode >= ARRAY_SIZE(usi_v2_modes)) > + return -EINVAL; > + > + val = usi_v2_modes[mode].val; > + ret = regmap_update_bits(usi->sysreg, usi->sw_conf, SW_CONF_MASK, val); > + if (ret) > + return ret; > + > + usi->mode = mode; > + dev_dbg(usi->dev, "USIv2 protocol: %s\n", usi_v2_modes[usi->mode].name); > + > + return 0; > +} > + > +/** > + * usi_v2_enable - Initialize USI block > + * @usi: USI driver object > + * > + * USI IP-core start state is "reset" (on startup and after CPU resume). This > + * routine enables USI block by clearing the reset flag. It also configures > + * HWACG behavior (needed e.g. for UART Rx). It should be performed before > + * underlying protocol becomes functional. > + * > + * Both 'pclk' and 'ipclk' clocks should be enabled when running this function. > + */ > +static void usi_v2_enable(const struct usi_v2 *usi) > +{ > + u32 val; > + > + /* Enable USI block */ > + val = readl(usi->regs + USI_CON); > + val &= ~USI_CON_RESET; > + writel(val, usi->regs + USI_CON); > + udelay(1); > + > + /* Continuously provide the clock to USI IP w/o gating */ > + if (usi->clkreq_on) { > + val = readl(usi->regs + USI_OPTION); > + val &= ~USI_OPTION_CLKSTOP_ON; > + val |= USI_OPTION_CLKREQ_ON; > + writel(val, usi->regs + USI_OPTION); > + } > +} > + > +static int usi_v2_configure(struct usi_v2 *usi) > +{ > + int ret; > + > + ret = clk_prepare_enable(usi->pclk); > + if (ret) > + return ret; > + > + ret = clk_prepare_enable(usi->ipclk); > + if (ret) > + goto err_pclk; > + > + ret = usi_v2_set_sw_conf(usi, usi->mode); > + if (ret) > + goto err_ipclk; > + > + usi_v2_enable(usi); > + > +err_ipclk: > + clk_disable_unprepare(usi->ipclk); > +err_pclk: > + clk_disable_unprepare(usi->pclk); > + return ret; > +} > + > +static int usi_v2_parse_dt(struct device_node *np, struct usi_v2 *usi) > +{ > + int ret; > + u32 mode; > + > + ret = of_property_read_u32(np, "samsung,mode", &mode); > + if (ret) > + return ret; > + usi->mode = mode; Parse and validate mode here, instead of usi_v2_set_sw_conf(). We expect DT to be correct, so if it is not, then there is no point to probe the device. Best regards, Krzysztof
On 28/11/2021 04:15, David Virag wrote: > On Sun, 2021-11-28 at 00:32 +0200, Sam Protsenko wrote: >> USIv2 IP-core provides selectable serial protocol (UART, SPI or >> High-Speed I2C); only one can be chosen at a time. This series >> implements USIv2 driver, which allows one to select particular USI >> function in device tree, and also performs USI block initialization. >> >> With that driver implemented, it's not needed to do USI >> initialization >> in protocol drivers anymore, so that code is removed from the serial >> driver. >> > > I think the downstream way of doing this (USI node reg being on the > SW_CONF register itself rather than an offset from uart/i2c/spi, the > USI driver only controlling the SW_CONF, and the uart/i2c/spi drivers > controlling their USI_CON and USI_OPTION regs) is cleaner, better, and > easier to adapt to USIv1 too. > > For example: I'm sure this is the case on USIv2 devices too, but on > Exynos7885, different devices have USI modes configured differently. > For example a Samsung Galaxy A8 (2018) has all the USI blocks > configured as SPI while a Samsung Galaxy M20 has the first USI > configured as dual HSI2C, the second as HSI2C on the first 2 pins and > the third as HSI2C on the last 2 pins. With this way of doing > everything on USIv2 we'd need 3 disabled USIv2 nodes in the SoC DTSI > for one USI block, each for every protocol the USI block can do, all > having a single child for their protocol and each referencing the same > sysreg (not even sure if that's even supported). Then the board DTS > could enable the USI node it needs. It's not supported (one cannot have three same nodes with same unit addresses), so this would be solved by dropping out unused interfaces, commenting them out or storing everything under one USI: usi@0x1abcdef0 { serial@.... { status = "okay"; } i2c@.... { status = "disabled"; } spi@.... { status = "disabled"; } } > > With the downstream way we could have just one USI node and we could > add the 3 protocols it can do disabled as seperate or child nodes. This > way the board DTS only needs to set the appropriate mode setting and > enable the protocol it needs. I'd say much better than having 3 USI > nodes for the same USI block. Then however you need to handle probe ordering and possible probe deferrals. > > Also this way is pretty USIv2 centric. Adding USIv1 support to this > driver is difficult this way because of the the lack of USI_CON and > USI_OPTION registers as a whole (so having nowhere to actually set the > reg of the USI node to, as the only thing USIv1 has is the SW_CONF > register). How is it difficult? Not having a register is easy - noop on given platform. > In my opinion being able to use the same driver and same > device tree layout for USIv1 and USIv2 is a definite plus > > The only real drawback of that way is having to add code for USIv2 > inside the UART, HSI2C, and SPI drivers but in my opinion the benefits > overweigh the drawbacks greatly. We could even make the uart/spi/hsi2c > drivers call a helper function in the USI driver to set their USI_CON > and USI_OPTION registers up so that code would be shared and not > duplicated. Wether this patch gets applied like this is not my choice > though, I'll let the people responsible decide > :-) > > Anyways, soon enough I can write an USIv1 driver after I submit all the > 7885 stuff I'm working on currently. If you want to, you can add USIv2 > support to that driver, or if an USIv2 driver is already in upstream at > that point, if it is written in the downstream way I can add v1 support > to that, or if it's like this I'll have to make a whole seperate driver > with a whole seperate DT structure. > > Best regards, > David > Best regards, Krzysztof
On Sun, 28 Nov 2021 at 05:15, David Virag <virag.david003@gmail.com> wrote: > > On Sun, 2021-11-28 at 00:32 +0200, Sam Protsenko wrote: > > USIv2 IP-core provides selectable serial protocol (UART, SPI or > > High-Speed I2C); only one can be chosen at a time. This series > > implements USIv2 driver, which allows one to select particular USI > > function in device tree, and also performs USI block initialization. > > > > With that driver implemented, it's not needed to do USI > > initialization > > in protocol drivers anymore, so that code is removed from the serial > > driver. > > > > I think the downstream way of doing this (USI node reg being on the > SW_CONF register itself rather than an offset from uart/i2c/spi, the > USI driver only controlling the SW_CONF, and the uart/i2c/spi drivers > controlling their USI_CON and USI_OPTION regs) is cleaner, better, and > easier to adapt to USIv1 too. > One reason why I think it's better to provide SW_CONF register via syscon node, is that it helps us to avoid possible register access conflicts in future, and also conflicts when requesting corresponding resources. In other words, the System Register block can be used by many consumers (drivers) in future; those consumers might try to modify the same registers simultaneously, which might lead to race conditions (as RMW operation is not atomic), so some kind of serialization should be done (like locking in regmap), which is provided by syscon. Also, that wouldn't even come to that: you just can't request the same I/O area twice in Linux. So if SW_CONF is passed via "reg" property to USI driver, and then we try to map the whole System Register (or its portion that includes SW_CONF), that request would fail. Although passing one SW_CONF register via "reg" might look easier to implement, it might also bring us all sort of problems later on. And I think a good design should account for such pitfalls. As for the USI registers: I really don't think that duplicating the code for USI block reset across uart/i2c/spi drivers would help us to accomplish anything. Why those drivers should be even aware of USI reset? At least in USIv2 block, the USI registers and uart/i2c/spi registers are not mixed: they are located at different and always fixed addresses. We can benefit from that fact, and provide Device Tree structure which reflects the hardware one, separating USI control from actual protocol nodes. > For example: I'm sure this is the case on USIv2 devices too, but on > Exynos7885, different devices have USI modes configured differently. > For example a Samsung Galaxy A8 (2018) has all the USI blocks > configured as SPI while a Samsung Galaxy M20 has the first USI > configured as dual HSI2C, the second as HSI2C on the first 2 pins and > the third as HSI2C on the last 2 pins. With this way of doing > everything on USIv2 we'd need 3 disabled USIv2 nodes in the SoC DTSI > for one USI block, each for every protocol the USI block can do, all > having a single child for their protocol and each referencing the same > sysreg (not even sure if that's even supported). Then the board DTS > could enable the USI node it needs. > If I'm following you correctly, then it's not like that. I guess Krzysztof already replied to that, so I'll probably just repeat his words. In that case you'll have something like this in your SoC dtsi (for your USIv1 case of course, because dual HSI2C is not present in USIv2): <<<<<<<<<<<<<<<<<<<<<<<<< cut here >>>>>>>>>>>>>>>>>>>>>>> usi1 { spi1 { }; hsi2c1_1 { }; hsi2c1_2 { }; }; usi2 { spi2 { }; hsi2c2_1 { }; }; usi3 { spi3 { }; hsi2c2_2 { }; }; <<<<<<<<<<<<<<<<<<<<<<<<< cut here >>>>>>>>>>>>>>>>>>>>>>> and then in your board dts you just have to enable corresponding usi's with proper modes, and enable chosen protocol nodes, like this: <<<<<<<<<<<<<<<<<<<<<<<<< cut here >>>>>>>>>>>>>>>>>>>>>>> &usi1 { status = "okay" samsung,mode = <USI_V1_DUAL_I2C>; }; &hsi2c1_1 { status = "okay" }; &hsi2c1_2 { status = "okay" }; <<<<<<<<<<<<<<<<<<<<<<<<< cut here >>>>>>>>>>>>>>>>>>>>>>> > With the downstream way we could have just one USI node and we could > add the 3 protocols it can do disabled as seperate or child nodes. This > way the board DTS only needs to set the appropriate mode setting and > enable the protocol it needs. I'd say much better than having 3 USI > nodes for the same USI block. > Not sure if with downstream USI driver you can actually have protocols as sub-nodes in USI node though. It doesn't do anything like of_platform_populate(). Also, with this USIv2 driver you can do the same thing you described: you can have just one USI node with 3 protocols as sub-nodes (or you can even have protocol nodes outside of USI node, but I'd not recommend that). Actually I can see that it's my fault for not describing that case in bindings example. I'll make sure to do that in v2. You also got me thinking about default mode: sometimes SW_CONF reset value chooses some protocol. In that case maybe it'd useful to have something like USI_V2_DEFAULT, to tell driver to not touch SW_CONF at all. And also I can add USI_V2_NONE while at it, so that driver can write 0x0 to SW_CONF: that way no protocol will be selected. Maybe that can be beneficial for PM reasons, if some board doesn't use some USI blocks at all. Do you think it's feasible to add those two values to dt-bindings header? And is it possible to do so in USIv1? > Also this way is pretty USIv2 centric. Adding USIv1 support to this > driver is difficult this way because of the the lack of USI_CON and > USI_OPTION registers as a whole (so having nowhere to actually set the > reg of the USI node to, as the only thing USIv1 has is the SW_CONF > register). In my opinion being able to use the same driver and same > device tree layout for USIv1 and USIv2 is a definite plus > Well, it's USIv2 driver after all. I never expected it can be extended for USIv1 support. If you think it can be reused for USIv1, it's fine by me. But we need to consider next things: - rename the driver to just "usi.c" (and also its configuration symbol) - provide different compatible for USIv1 (and maybe corresponding driver data) - rework bindings (header and doc); make sure existing bindings are intact (we shouldn't change already introduced interfaces) - in case of USIv1 compatible; don't try to tinker with USIv2 registers - samsung,clkreq-on won't be available in case of USIv1 compatible Because I don't have USIv1 SoC TRM (and neither do I possess some USIv1 board which I can use for test), I don't think it's my place to add USIv1 support. But I think it's possible to do so, using my input above. I can see how it might be frustrating having to do some extra work (comparing to just using the code existing in downstream). But I guess that's the difference: vendor is mostly concerned about competitive advantage and getting to market fast, while upstream is more concerned about quality, considering all use cases, and having proper design. Anyway, we can work together to make it right, and to have both IP-cores support. In the worst case, if those are too different, we can have two separate drivers for those. > The only real drawback of that way is having to add code for USIv2 > inside the UART, HSI2C, and SPI drivers but in my opinion the benefits > overweigh the drawbacks greatly. We could even make the uart/spi/hsi2c > drivers call a helper function in the USI driver to set their USI_CON > and USI_OPTION registers up so that code would be shared and not > duplicated. Wether this patch gets applied like this is not my choice > though, I'll let the people responsible decide > :-) > I'd argue that there are a lot of real drawbacks of using downstream driver as is. That's why I completely re-designed and re-implemented it. Downstream driver can't be built and function as a module, it doesn't respect System Register sharing between consumers, it leads to USI reset code duplication scattered across protocol drivers (that arguably shouldn't even be aware of that), it doesn't reflect HW structure clearly, it's not holding clocks needed for registers access (btw, sysreg clock can be provided in syscon node, exactly for that reason). As Krzysztof said, it also can't handle correct probe order and deferred probes. Downstream driver might work fine for some particular use-cases the vendor has, but in upstream it's better to cover more cases we can expect, as upstream kernel is used on more platforms, with more user space variants, etc. I don't really think protocol drivers should be aware of USI registers at all, but if we they do -- we can provide some API from USIv2 driver later, with EXPORT_SYMBOL(), referencing corresponding USI instance by phandle or using some other mechanism for inter-driver communication. Of course, it's not my place to decide on patch acceptance too. But I was under the impression that maintainers would be ok with this course of actions. Also, upstream kernel seems to already follow the same design for some similar drivers. See for example drivers/soc/qcom/qcom_gsbi.c. > Anyways, soon enough I can write an USIv1 driver after I submit all the > 7885 stuff I'm working on currently. If you want to, you can add USIv2 > support to that driver, or if an USIv2 driver is already in upstream at > that point, if it is written in the downstream way I can add v1 support > to that, or if it's like this I'll have to make a whole seperate driver > with a whole seperate DT structure. > If it's like you said (USIv1 only touches the SW_CONF register), I guess USIv2 driver can be extended for USIv1 case. I already provided my thoughts on such rework above. It's probably better to consult with Krzysztof first. I guess the only way to figure out if it's feasible or it's better to have separate exynos-usi-v1.c for USIv1, is to try and add USIv1 support into USIv2 driver and see how pretty or ugly it is :) Whatever the way you decide to go with, please add me to Cc list when sending USIv1 patches. > Best regards, > David
On Mon, 29 Nov 2021 at 10:52, Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> wrote: > > On 27/11/2021 23:32, Sam Protsenko wrote: > > Enable serial driver to be built as a module. To do so, init the console > > support on driver/module load instead of using console_initcall(). > > > > This is needed for proper support of USIv2 driver (which can be built as > > a module, which in turn makes SERIAL_SAMSUNG be a module too). It also > > might be useful for Android GKI modularization efforts. > > > > Inspired by commit 87a0b9f98ac5 ("tty: serial: meson: enable console as > > module"). > > > > Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> > > --- > > drivers/tty/serial/Kconfig | 2 +- > > drivers/tty/serial/samsung_tty.c | 21 +++++++++++++++++++-- > > 2 files changed, 20 insertions(+), 3 deletions(-) > > > > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig > > index fc543ac97c13..0e5ccb25bdb1 100644 > > --- a/drivers/tty/serial/Kconfig > > +++ b/drivers/tty/serial/Kconfig > > @@ -263,7 +263,7 @@ config SERIAL_SAMSUNG_UARTS > > > > config SERIAL_SAMSUNG_CONSOLE > > bool "Support for console on Samsung SoC serial port" > > - depends on SERIAL_SAMSUNG=y > > + depends on SERIAL_SAMSUNG > > select SERIAL_CORE_CONSOLE > > select SERIAL_EARLYCON > > help > > diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c > > index f986a9253dc8..92a63e9392ed 100644 > > --- a/drivers/tty/serial/samsung_tty.c > > +++ b/drivers/tty/serial/samsung_tty.c > > @@ -1720,10 +1720,10 @@ static int __init s3c24xx_serial_console_init(void) > > register_console(&s3c24xx_serial_console); > > return 0; > > } > > -console_initcall(s3c24xx_serial_console_init); > > > > #define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console > > #else > > +static inline int s3c24xx_serial_console_init(void) { return 0; } > > #define S3C24XX_SERIAL_CONSOLE NULL > > #endif > > > > @@ -2898,7 +2898,24 @@ static struct platform_driver samsung_serial_driver = { > > }, > > }; > > > > -module_platform_driver(samsung_serial_driver); > > +static int __init samsung_serial_init(void) > > +{ > > + int ret; > > + > > + ret = s3c24xx_serial_console_init(); > > + if (ret) > > + return ret; > > This will trigger warns on module re-loading, won't it? Either suppress > unbind or cleanup in module exit. > I guess that's already taken care of in samsung_serial_remove(): it's doing uart_remove_one_port(), which in turn does unregister_console(). So I don't think anything extra should be done on module exit. Or I'm missing something? That case (unload/load) actually doesn't work well in my case: serial console doesn't work after doing "modprobe -r samsung_tty; modprobe samsung_tty" (but it works fine e.g. in case of i2c_exynos5 driver). Not sure what is wrong, but I can see that my board keeps running (heartbeat LED is still blinking). Not even sure if that use case (unload/load) was ever functional before. Anyway, please let me know if you think something should be done about this particular patch. Right now I don't see anything missing. > > + > > + return platform_driver_register(&samsung_serial_driver); > > +} > > + > > +static void __exit samsung_serial_exit(void) > > +{ > > + platform_driver_unregister(&samsung_serial_driver); > > +} > > + > > +module_init(samsung_serial_init); > > +module_exit(samsung_serial_exit); > > > > #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE > > /* > > > > > Best regards, > Krzysztof
On Mon, 29 Nov 2021 at 22:18, Sam Protsenko <semen.protsenko@linaro.org> wrote: > > On Mon, 29 Nov 2021 at 10:52, Krzysztof Kozlowski > <krzysztof.kozlowski@canonical.com> wrote: > > > > On 27/11/2021 23:32, Sam Protsenko wrote: > > > Enable serial driver to be built as a module. To do so, init the console > > > support on driver/module load instead of using console_initcall(). > > > > > > This is needed for proper support of USIv2 driver (which can be built as > > > a module, which in turn makes SERIAL_SAMSUNG be a module too). It also > > > might be useful for Android GKI modularization efforts. > > > > > > Inspired by commit 87a0b9f98ac5 ("tty: serial: meson: enable console as > > > module"). > > > > > > Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> > > > --- > > > drivers/tty/serial/Kconfig | 2 +- > > > drivers/tty/serial/samsung_tty.c | 21 +++++++++++++++++++-- > > > 2 files changed, 20 insertions(+), 3 deletions(-) > > > > > > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig > > > index fc543ac97c13..0e5ccb25bdb1 100644 > > > --- a/drivers/tty/serial/Kconfig > > > +++ b/drivers/tty/serial/Kconfig > > > @@ -263,7 +263,7 @@ config SERIAL_SAMSUNG_UARTS > > > > > > config SERIAL_SAMSUNG_CONSOLE > > > bool "Support for console on Samsung SoC serial port" > > > - depends on SERIAL_SAMSUNG=y > > > + depends on SERIAL_SAMSUNG > > > select SERIAL_CORE_CONSOLE > > > select SERIAL_EARLYCON > > > help > > > diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c > > > index f986a9253dc8..92a63e9392ed 100644 > > > --- a/drivers/tty/serial/samsung_tty.c > > > +++ b/drivers/tty/serial/samsung_tty.c > > > @@ -1720,10 +1720,10 @@ static int __init s3c24xx_serial_console_init(void) > > > register_console(&s3c24xx_serial_console); > > > return 0; > > > } > > > -console_initcall(s3c24xx_serial_console_init); > > > > > > #define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console > > > #else > > > +static inline int s3c24xx_serial_console_init(void) { return 0; } > > > #define S3C24XX_SERIAL_CONSOLE NULL > > > #endif > > > > > > @@ -2898,7 +2898,24 @@ static struct platform_driver samsung_serial_driver = { > > > }, > > > }; > > > > > > -module_platform_driver(samsung_serial_driver); > > > +static int __init samsung_serial_init(void) > > > +{ > > > + int ret; > > > + > > > + ret = s3c24xx_serial_console_init(); > > > + if (ret) > > > + return ret; > > > > This will trigger warns on module re-loading, won't it? Either suppress > > unbind or cleanup in module exit. > > > > I guess that's already taken care of in samsung_serial_remove(): it's > doing uart_remove_one_port(), which in turn does unregister_console(). > So I don't think anything extra should be done on module exit. Or I'm > missing something? > > That case (unload/load) actually doesn't work well in my case: serial > console doesn't work after doing "modprobe -r samsung_tty; modprobe > samsung_tty" (but it works fine e.g. in case of i2c_exynos5 driver). > Not sure what is wrong, but I can see that my board keeps running > (heartbeat LED is still blinking). Not even sure if that use case > (unload/load) was ever functional before. > > Anyway, please let me know if you think something should be done about > this particular patch. Right now I don't see anything missing. > ...But I'll actually add proper error path handling in samsung_serial_init(), i.e. unregister console if platform_driver_register() fails. And I'll add the same console unregister in samsung_serial_exit(), just in case. > > > + > > > + return platform_driver_register(&samsung_serial_driver); > > > +} > > > + > > > +static void __exit samsung_serial_exit(void) > > > +{ > > > + platform_driver_unregister(&samsung_serial_driver); > > > +} > > > + > > > +module_init(samsung_serial_init); > > > +module_exit(samsung_serial_exit); > > > > > > #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE > > > /* > > > > > > > > > Best regards, > > Krzysztof