Hi Krzysztof,
On 12/13/24 3:55 PM, Krzysztof Kozlowski wrote:
On 13/12/2024 10:42, Markuss Broks wrote:
Hi Krzysztof,
On 12/13/24 9:49 AM, Krzysztof Kozlowski wrote:
On 12/12/2024 22:09, Markuss Broks wrote:
Add a driver for Samsung SPEEDY serial bus host controller.
SPEEDY is a proprietary 1 wire serial bus used by Samsung
in various devices (usually mobile), like Samsung Galaxy
phones. It is usually used for connecting PMIC or various
other peripherals, like audio codecs or RF components.
This bus can address at most 1MiB (4 bit device address,
8 bit registers per device, 8 bit wide registers:
256*256*16 = 1MiB of address space.
Co-developed-by: Maksym Holovach <nergzd@xxxxxxxxxxxxx>
Signed-off-by: Maksym Holovach <nergzd@xxxxxxxxxxxxx>
Signed-off-by: Markuss Broks <markuss.broks@xxxxxxxxx>
---
drivers/soc/samsung/Kconfig | 13 +
drivers/soc/samsung/Makefile | 2 +
drivers/soc/samsung/exynos-speedy.c | 457 ++++++++++++++++++++++++++++++
include/linux/soc/samsung/exynos-speedy.h | 56 ++++
4 files changed, 528 insertions(+)
diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index 1a5dfdc978dc4069eb71c4e8eada7ff1913b86b3..a38150fc9999ded1e1e93e2a9ef43b88175d34bd 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -49,6 +49,19 @@ config EXYNOS_PMU_ARM_DRIVERS
bool "Exynos PMU ARMv7-specific driver extensions" if COMPILE_TEST
depends on EXYNOS_PMU
+config EXYNOS_SPEEDY
+ tristate "Exynos SPEEDY host controller driver"
+ depends on ARCH_EXYNOS || COMPILE_TEST
+ depends on OF
+ depends on REGMAP_MMIO
+ help
+ Enable support for Exynos SPEEDY host controller block.
+ SPEEDY is a 1 wire proprietary Samsung serial bus, found in
+ modern Samsung Exynos SoCs, like Exynos8895 and newer.
I did not check that much but this looks like 1wire for which we have
subsystem. Please investigate more and figure out the differences.
This is not compatible with Dallas Semi 1-Wire bus. There are several
differences but the phy level is not compatible, looking at the Samsung
patent. [1] The most obvious difference is that 1-Wire is discoverable,
and this bus isn't. I'm pretty sure this is Samsung's own solution to a
serial interface through only one wire.
It's fine then.
+
+ Select this if you have a Samsung Exynos device which uses
+ SPEEDY bus.
+
+
+/* SPEEDY_PACKET_GAP_TIME register bits */
+#define SPEEDY_FIFO_TX_ALMOST_EMPTY (1 << 4)
+#define SPEEDY_FIFO_RX_ALMOST_FULL (1 << 8)
+#define SPEEDY_FSM_INIT (1 << 1)
+#define SPEEDY_FSM_TX_CMD (1 << 2)
+#define SPEEDY_FSM_STANDBY (1 << 3)
+#define SPEEDY_FSM_DATA (1 << 4)
+#define SPEEDY_FSM_TIMEOUT (1 << 5)
+#define SPEEDY_FSM_TRANS_DONE (1 << 6)
+#define SPEEDY_FSM_IO_RX_STAT_MASK (3 << 7)
+#define SPEEDY_FSM_IO_TX_IDLE (1 << 9)
+#define SPEEDY_FSM_IO_TX_GET_PACKET (1 << 10)
+#define SPEEDY_FSM_IO_TX_PACKET (1 << 11)
+#define SPEEDY_FSM_IO_TX_DONE (1 << 12)
+
+#define SPEEDY_RX_LENGTH(n) ((n) << 0)
+#define SPEEDY_TX_LENGTH(n) ((n) << 8)
+
+#define SPEEDY_DEVICE(x) ((x & 0xf) << 15)
+#define SPEEDY_ADDRESS(x) ((x & 0xff) << 7)
+
+static const struct of_device_id speedy_match[] = {
+ { .compatible = "samsung,exynos9810-speedy" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, speedy_match);
This is never at top of the file, but immediately before driver
structure. Look at other drivers.
The function speedy_get_device uses this to match the compatible, do I
just leave the prototype here?
1. Entire speedy_get_device() is unused so it will be removed.
2. Even if it stays, speedy_get_device() is not supposed to match
anything. How are you supposed to use Samsung PMIC on different
controller? These things should not be tied.
speedy_get_device was my approach to not make it a proper bus driver,
since I've thought it would be overkill for the purposes.
speedy_get_device() is supposed to be called by a child device through
the devm_ helper, and what it does is: it gets the parent node, verifies
if that node matches the speedy compatible, then if it does it gets the
platform_device and crafts a new struct speedy_device for the child
device to use. This approach is perhaps hacky, but it helps simplify
things a bit by not implementing the whole bus logic properly, let me
know if it's too hacky...
Actually, one of my other concerns is that S2MPS18 has both SPEEDY and
I2C interface for communicating with host, and with current approach
it's going to be hacky if we want to support both. I've not seen S2MPS18
used with I2C on any real boards, but it should really be trivial to
support both, and with current hacky approach it might be not trivial at
all...
+
+static const struct regmap_config speedy_map_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+};
...
+ cmd = SPEEDY_ACCESS_RANDOM | SPEEDY_DIRECTION_READ |
+ SPEEDY_DEVICE(reg) | SPEEDY_ADDRESS(addr);
+
+ int_ctl = SPEEDY_TRANSFER_DONE_EN | SPEEDY_FIFO_RX_ALMOST_FULL_EN |
+ SPEEDY_RX_FIFO_INT_TRAILER_EN | SPEEDY_RX_MODEBIT_ERR_EN |
+ SPEEDY_RX_GLITCH_ERR_EN | SPEEDY_RX_ENDBIT_ERR_EN |
+ SPEEDY_REMOTE_RESET_REQ_EN;
+
+ ret = speedy_int_clear(speedy);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(speedy->map, SPEEDY_INT_ENABLE, int_ctl);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(speedy->map, SPEEDY_CMD, cmd);
+ if (ret)
+ return ret;
+
+ /* Wait for xfer done */
+ ret = regmap_read_poll_timeout(speedy->map, SPEEDY_INT_STATUS, int_status,
+ int_status & SPEEDY_TRANSFER_DONE, 5000, 50000);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(speedy->map, SPEEDY_RX_DATA, val);
+ if (ret)
+ return ret;
+
+ ret = speedy_int_clear(speedy);
+
+ mutex_unlock(&speedy->io_lock);
+
+ return ret;
+}
+
+int exynos_speedy_read(const struct speedy_device *device, u32 addr, u32 *val)
+{
+ return _speedy_read(device->speedy, device->reg, addr, val);
+}
+EXPORT_SYMBOL_GPL(exynos_speedy_read);
Nope, drop, unused.
This is intended to be used with other device drivers, this driver in
itself doesn't do anything, it only configures the controller and makes
it ready for transmitting data, it's other drivers that will come (e.g.
S2MPS18 PMIC, which uses SPEEDY for communication with the SoC) that
will utilize those functions.
Post entire series, so we see users of this API. If you post API without
users, it won't be accepted simply because there are no users and we do
not want dead, unused code.
Anyway we must see how you intend to use that interface to properly
review it.
Sure!
+
+/**
+ * _speedy_write() - internal speedy write operation
+ * @speedy: pointer to speedy controller struct
+ * @reg: address of device on the bus
+ * @addr: address to write
+ * @val: value to write
+ *
...
+}
+
+static struct platform_driver speedy_driver = {
+ .probe = speedy_probe,
+ .driver = {
+ .name = "exynos-speedy",
+ .of_match_table = speedy_match,
+ },
+};
+
+module_platform_driver(speedy_driver);
+
+MODULE_DESCRIPTION("Samsung Exynos SPEEDY host controller driver");
+MODULE_AUTHOR("Markuss Broks <markuss.broks@xxxxxxxxx>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/soc/samsung/exynos-speedy.h b/include/linux/soc/samsung/exynos-speedy.h
new file mode 100644
index 0000000000000000000000000000000000000000..b2857d65d3b50927373866dd8ae1c47e98af6d7b
--- /dev/null
+++ b/include/linux/soc/samsung/exynos-speedy.h
Drop the header, not used.
Same here, please clarify how this should be handled. This driver
implements the devm_speedy_get_device and read/write functions for its
child devices in that header, future drivers for e.g. PMIC would use
this header and call devm_speedy_get_device to get a speedy_device
pointer and then use read/write functions to read/write from the bus.
This needs to be proper bus with proper speedy_driver clients. See how
other buses - struct bus_type. Recent example of bus using platform
drivers for devices would be pwrseq (power/sequence). Not so old other
bus using its own xxx_driver for devices could be cxl or ffa (arm_ffa).
This current non-bus approach, could also work if this is really Samsung
specific. See memory/ for examples of MMIO buses and MMIO clients.
Let me know if the current approach is acceptable or not, it is fine in
my eyes if it needs to be a full-fledged bus driver, but in my opinion
it's a lot of effort and additional logic for a niche samsung-specific
usecase. Although I've heard that they're still using SPEEDY in newer
devices as well, perhaps for more advanced purposes than just an
interface for PMIC, which would mean additional things to consider...
I'll be glad to hear how you think it should be implemented, because I
myself do not know a lot about kernel drivers like this...
I don't know good examples of non-MMIO buses not using bus_type and I am
not sure whether this is accepted in general. I'll ask on IRC, maybe
someone will give some hints.
Best regards,
Krzysztof
Thanks,
- Markuss