Hi Kamel, On Mon, Nov 13, 2023 at 02:32:12PM +0100, kamel.bouhara@xxxxxxxxxxx wrote: > Le 2023-10-22 23:54, Jeff LaBundy a écrit : > > Hi Kamel, > > Hi Jeff, > > > > > On Thu, Oct 12, 2023 at 09:40:34AM +0200, Kamel Bouhara wrote: > > > Add a new driver for the TouchNetix's axiom family of > > > touchscreen controllers. This driver only supports i2c > > > and can be later adapted for SPI and USB support. > > > > > > Signed-off-by: Kamel Bouhara <kamel.bouhara@xxxxxxxxxxx> > > > --- > > > MAINTAINERS | 1 + > > > drivers/input/touchscreen/Kconfig | 13 + > > > drivers/input/touchscreen/Makefile | 1 + > > > .../input/touchscreen/touchnetix_axiom_i2c.c | 740 > > > ++++++++++++++++++ > > > 4 files changed, 755 insertions(+) > > > create mode 100644 drivers/input/touchscreen/touchnetix_axiom_i2c.c > > > > Please do not include 'i2c' in the filename. If the driver is expanded > > in > > the future to support SPI, it would make sense to have > > touchnetix_axiom.c, > > touchnetix_axiom_i2c.c and touchnetix_axiom_spi.c. To prevent this > > driver > > from having to be renamed in that case, just call it touchnetix_axiom.c. > > > > Sure but the generic part of the code could also be moved to > touchnetix_axiom.c. Right; I'm simply saying to do this now as opposed to having a giant patch later when SPI support comes along. In case I have misunderstood your reply, please let me know. [...] > > > +#include <linux/crc16.h> > > > +#include <linux/delay.h> > > > +#include <linux/device.h> > > > +#include <linux/gpio/consumer.h> > > > +#include <linux/i2c.h> > > > +#include <linux/input.h> > > > +#include <linux/input/mt.h> > > > +#include <linux/interrupt.h> > > > +#include <linux/kernel.h> > > > +#include <linux/module.h> > > > > Please #include mod_devicetable.h as well. > > > > OK is this only for the sake of clarity ? As mod_devicetable.h is already > included in linux/of.h ? I haphazardly wrote this comment while in the process of reviewing dbce1a7d5dce ("Input: Explicitly include correct DT includes"); however you are correct. That being said, do you really need the entire of.h for this driver? > > > > +#include <linux/of.h> > > > +#include <linux/pm.h> [...] > > > +static int > > > +axiom_i2c_read(struct i2c_client *client, u8 usage, u8 page, u8 > > > *buf, u16 len) > > > +{ > > > + struct axiom_data *ts = i2c_get_clientdata(client); > > > + struct axiom_cmd_header cmd_header; > > > + struct i2c_msg msg[2]; > > > + int ret; > > > + > > > + cmd_header.target_address = > > > cpu_to_le16(usage_to_target_address(ts, usage, page, 0)); > > > + cmd_header.length = cpu_to_le16(len); > > > + cmd_header.read = 1; > > > + > > > + msg[0].addr = client->addr; > > > + msg[0].flags = 0; > > > + msg[0].len = sizeof(cmd_header); > > > + msg[0].buf = (u8 *)&cmd_header; > > > + > > > + msg[1].addr = client->addr; > > > + msg[1].flags = I2C_M_RD; > > > + msg[1].len = len; > > > + msg[1].buf = (char *)buf; > > > > Again, please use u8 in place of char, as was done for the first > > element. > > OK. > > > > > > + > > > + ret = i2c_transfer(client->adapter, msg, 2); > > > > Please use ARRAY_SIZE(msg) above as you do below. > > > > > + if (ret != ARRAY_SIZE(msg)) { > > > + dev_err(&client->dev, > > > + "Failed reading usage %#x page %#x, error=%d\n", > > > + usage, page, ret); > > > + return -EIO; > > > + } > > > > This check papers over negative error codes that may have been returned > > by > > i2c_transfer(). For ret < 0 you should return ret, and only return -EIO > > for > > 0 <= ret < ARRAY_SIZE(msg). > > > > More importantly, however, if this device supports multiple transports > > and > > you expect SPI support can be added in the future, you really should use > > regmap throughout in order to avoid ripping up this driver later. > > > > I have a doubt on wether or not regmap can be used for SPI as there is some > specific padding required for SPI. You can still define your own read and write callbacks in the small SPI driver, leaving generic regmap calls in the core driver. > > > > + > > > + return 0; > > > +} > > > + > > > +static int > > > +axiom_i2c_write(struct i2c_client *client, u8 usage, u8 page, u8 > > > *buf, u16 len) > > > +{ > > > + struct axiom_data *ts = i2c_get_clientdata(client); > > > + struct axiom_cmd_header cmd_header; > > > + struct i2c_msg msg[2]; > > > + int ret; > > > + > > > + cmd_header.target_address = > > > cpu_to_le16(usage_to_target_address(ts, usage, page, 0)); > > > + cmd_header.length = cpu_to_le16(len); > > > + cmd_header.read = 0; > > > + > > > + msg[0].addr = client->addr; > > > + msg[0].flags = 0; > > > + msg[0].len = sizeof(cmd_header); > > > + msg[0].buf = (u8 *)&cmd_header; > > > + > > > + msg[1].addr = client->addr; > > > + msg[1].flags = 0; > > > + msg[1].len = len; > > > + msg[1].buf = (char *)buf; > > > + > > > + ret = i2c_transfer(client->adapter, msg, 2); > > > + if (ret < 0) { > > > + dev_err(&client->dev, > > > + "Failed to write usage %#x page %#x, error=%d\n", usage, > > > + page, ret); > > > + return ret; > > > + } > > > > The error handling between your read and write wrappers is inconsistent; > > please see my comment above. > > > > Is there any reason i2c_master_send() cannot work here? I'm guessing the > > controller needs a repeated start in between the two messages? > > > > Yes reads requires repeated starts between each messages. > > For writes I could still use i2c_master_send() but what makes it more > relevant here ? The code can be a bit smaller in terms of lines with the header and payload copied into a small buffer and written with i2c_master_send(), but this is fine too. [...] > > > For these kind of special requirements, it's helpful to add some > > comments > > as to why the HW calls for additional housekeeping. > > > > OK. > > > > + > > > + return 0; > > > +} > > > + > > > +/* > > > + * Decodes and populates the local Usage Table. > > > + * Given a buffer of data read from page 1 onwards of u31 from an > > > aXiom > > > + * device. > > > + */ > > > > What is a usage table? These comments aren't helpful unless some of the > > underlying concepts are defined as well. > > It's a set of registers regrouped in categories (data, configuration, device > and report). > > I'll try to clarify it. ACK, thanks. [...] > > > +/* Rebaseline the touchscreen, effectively zero-ing it */ > > > > What does it mean to rebaseline the touchscreen? I'm guessing it means > > to null out or normalize pressure? Please consider a less colloquialized > > function name. > > > > Out of curiousity, what happens if the user's hand happens to be on the > > touch surface at the time you call axiom_rebaseline()? Does the device > > recover on its own? > > This indeed force the controller to measure a new capacitance by zeoring it, > I don't really know if it's harmful, yet the documentation says rebaseline > is > for tuning or debug purpose. > > I believe this is done for testing the communication. ACK, thanks. > > > > > > +static int axiom_rebaseline(struct axiom_data *ts) > > > +{ > > > + char buffer[8] = {}; > > > > Are you expecting each element to be initialized to zero? > > Yes. ACK, I merely had not seen this method before. > > > > > > + > > > + buffer[0] = AXIOM_REBASELINE_CMD; > > > + > > > + return axiom_i2c_write(ts->client, AXIOM_REPORT_USAGE_ID, 0, > > > buffer, sizeof(buffer)); > > > +} > > > + > > > +static int axiom_i2c_probe(struct i2c_client *client) > > > +{ > > > + struct device *dev = &client->dev; > > > + struct input_dev *input_dev; > > > + struct axiom_data *ts; > > > + int ret; > > > + int target; > > > + > > > + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); > > > + if (!ts) > > > + return -ENOMEM; > > > + > > > + ts->client = client; > > > + i2c_set_clientdata(client, ts); > > > + ts->dev = dev; > > > + > > > + ts->irq_gpio = devm_gpiod_get_optional(dev, "irq", GPIOD_IN); > > > + if (IS_ERR(ts->irq_gpio)) > > > + return dev_err_probe(dev, PTR_ERR(ts->irq_gpio), "failed to get > > > irq GPIO"); > > > + > > > + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", > > > GPIOD_OUT_HIGH); > > > + if (IS_ERR(ts->reset_gpio)) > > > + return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to get > > > reset GPIO\n"); > > > + > > > + axiom_reset(ts->reset_gpio); > > > > We shouldn't call axiom_reset() if reset_gpio is NULL. Even though the > > calls to gpiod_set_value_cansleep() will bail safely, there is no reason > > to make the CPU sleep for 100 ms if the device was not actually reset. > > > > > + > > > + if (ts->irq_gpio) { > > > + ret = devm_request_threaded_irq(dev, client->irq, NULL, > > > + axiom_irq, 0, dev_name(dev), ts); > > > > Did you mean to set IRQF_ONESHOT? > > No OK, why not? This is a threaded handler and it's obviously not meant to be reentrant. Why is this implementation different than any other driver with a threaded handler? In case I have misunderstood, please let me know. Kind regards, Jeff LaBundy