From c74b34fa4df21624df9c9995ad00fc584b79a67b Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 8 Aug 2017 15:23:29 +0200 Subject: [PATCH 01/57] fbdev: ssd1306: abort probe if we cannot talk to the display We should abort probe if we cannot talk to the display. This prevents creating a fb device which does not work and prevents a lot of i2c communication error messages as a consequence. Signed-off-by: Olliver Schinagl --- drivers/video/fbdev/ssd1307fb.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index f599520374ddf5..438f0086abea10 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -290,6 +290,7 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) int ret; u32 precharge, dclk, com_invdir, compins; struct pwm_args pargs; + char status; if (par->device_info->need_pwm) { par->pwm = pwm_get(&par->client->dev, NULL); @@ -315,6 +316,13 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) par->pwm->pwm, par->pwm_period); }; + /* Check if we can talk to the display */ + ret = i2c_master_recv(par->client, &status, 1); + if (ret < 0) { + dev_err(&par->client->dev, "controller not found\n"); + return ret; + } + /* Set initial contrast */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); if (ret < 0) From 0bed2fc9c3bafcfbb31fd827d6299fa83d273aab Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 19 Dec 2017 16:46:15 +0100 Subject: [PATCH 02/57] fbdev: ssd1307: rename i2c device ids For autoprobing to work on devicetree based systems, the compatible name is used when probing i2c devices. Because the device id is now not the same, the ssd1307 does not get autoprobed. By renaming the device id, autoprobing is now functional on devicetree based systems. Signed-off-by: Olliver Schinagl --- drivers/video/fbdev/ssd1307fb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 438f0086abea10..93912ba01da194 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -756,10 +756,10 @@ static int ssd1307fb_remove(struct i2c_client *client) } static const struct i2c_device_id ssd1307fb_i2c_id[] = { - { "ssd1305fb", 0 }, - { "ssd1306fb", 0 }, - { "ssd1307fb", 0 }, - { "ssd1309fb", 0 }, + { "ssd1305fb-i2c", 0 }, + { "ssd1306fb-i2c", 0 }, + { "ssd1307fb-i2c", 0 }, + { "ssd1309fb-i2c", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); From 3b4ca6021a75e9be10cc7cadbfa2c4dc5ea8035e Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 18 Dec 2017 11:29:47 +0100 Subject: [PATCH 03/57] fbdev: ssd1306: make output a little more consistent The ssd1306 driver is a little inconsistent in the debug messages. Common is to not capitalize the first letter and to not add any periods etc at the end. Also add a quick shortcut to access the device structure shortening our error messages. Finally take this cleanup opportunity to add the missing device.h header. Signed-off-by: Olliver Schinagl --- drivers/video/fbdev/ssd1307fb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 93912ba01da194..4f3eab6d465785 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -291,11 +292,12 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) u32 precharge, dclk, com_invdir, compins; struct pwm_args pargs; char status; + struct device *dev = &par->client->dev; if (par->device_info->need_pwm) { - par->pwm = pwm_get(&par->client->dev, NULL); + par->pwm = pwm_get(dev, NULL); if (IS_ERR(par->pwm)) { - dev_err(&par->client->dev, "Could not get PWM from device tree!\n"); + dev_err(dev, "could not get PWM from device tree\n"); return PTR_ERR(par->pwm); } @@ -312,14 +314,14 @@ static int ssd1307fb_init(struct ssd1307fb_par *par) pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); pwm_enable(par->pwm); - dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n", + dev_dbg(dev, "using PWM%d with a %dns period\n", par->pwm->pwm, par->pwm_period); }; /* Check if we can talk to the display */ ret = i2c_master_recv(par->client, &status, 1); if (ret < 0) { - dev_err(&par->client->dev, "controller not found\n"); + dev_err(dev, "controller not found\n"); return ret; } From bc8c3e6c6ba23c85b219f37cbdcca02e32559783 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 18 Dec 2017 11:33:00 +0100 Subject: [PATCH 04/57] fbdev: ssd1306: use ratelimited error printing When we fail to send messages to our display, we print an error. However this 'spam' can continue heavily and add a heavy load on the system just to print these messages. Lets use the ratelimited variant of dev_err to lessen the system load. Signed-off-by: Olliver Schinagl --- drivers/video/fbdev/ssd1307fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index 4f3eab6d465785..7fa613f55cdc58 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -124,7 +124,7 @@ static int ssd1307fb_write_array(struct i2c_client *client, ret = i2c_master_send(client, (u8 *)array, len); if (ret != len) { - dev_err(&client->dev, "Couldn't send I2C command.\n"); + dev_err_ratelimited(&client->dev, "couldn't send I2C command\n"); return ret; } From e19c8b848af00307a54718afe96e5309c76b61c3 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 8 Aug 2017 11:32:40 +0200 Subject: [PATCH 05/57] leds: pca963x: abort probe if device is not connected Currently, the driver always successfully manages to load, even if there is no actual chip present (or communication is not possible over i2c). This has the side-effect, that the node is always available in sysfs, and writing data to it which results in i2c errors. To improve behavior here, we try to read the first byte from the device, and if this was okay, we at least know there is some device on the bus, even though we do not know which one. Signed-off-by: Olliver Schinagl --- drivers/leds/leds-pca963x.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 3bf9a127181927..e5af8db0339ea8 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -412,6 +412,13 @@ static int pca963x_probe(struct i2c_client *client, if (!pca963x) return -ENOMEM; + /* Check if chip actually exists on the bus */ + err = i2c_smbus_read_byte_data(client, PCA963X_MODE1); + if (err < 0) { + dev_err(&client->dev, "controller not found"); + return err; + } + i2c_set_clientdata(client, pca963x_chip); mutex_init(&pca963x_chip->mutex); From 683f5025bc758fbc35ead17f271e4e09ed958d81 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Fri, 29 May 2015 16:09:43 +0200 Subject: [PATCH 06/57] leds: pca963x: alphabetize headers Re-order headers so they are in alphabetical order. Signed-off-by: Olliver Schinagl --- drivers/leds/leds-pca963x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index e5af8db0339ea8..a62416038f4a8c 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -26,16 +26,16 @@ */ #include -#include -#include -#include #include -#include +#include #include #include -#include +#include +#include #include #include +#include +#include /* LED select registers determine the source that drives LED outputs */ #define PCA963X_LED_OFF 0x0 /* LED driver off */ From e516b80173ffdb0fcd7fe75b93775a6dd65dc507 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 16 Dec 2015 13:42:25 +0100 Subject: [PATCH 07/57] leds: pca963x: add defines and remove some magic values This patch adds some more defines so that the driver can receive a little more future work. These new defines are then used throughout the existing code the remove some magic values. This patch does not produce any binary changes. Signed-off-by: Olliver Schinagl --- drivers/leds/leds-pca963x.c | 149 ++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 47 deletions(-) diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index a62416038f4a8c..e420a270a3c985 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -37,17 +37,68 @@ #include #include -/* LED select registers determine the source that drives LED outputs */ -#define PCA963X_LED_OFF 0x0 /* LED driver off */ -#define PCA963X_LED_ON 0x1 /* LED driver on */ -#define PCA963X_LED_PWM 0x2 /* Controlled through PWM */ -#define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ - -#define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */ - -#define PCA963X_MODE1 0x00 -#define PCA963X_MODE2 0x01 -#define PCA963X_PWM_BASE 0x02 +#define PCA963X_MAX_NAME_LEN 32 + +#define PCA963X_MODE1 0x00 /* mode 1 register addr */ +#define PCA963X_MODE1_ALLCALL_ON BIT(0) /* respond to LED All Call */ +#define PCA963X_MODE1_RESPOND_SUB3 BIT(1) /* respond to Sub address 3 */ +#define PCA963X_MODE1_RESPOND_SUB2 BIT(2) /* respond to Sub address 2 */ +#define PCA963X_MODE1_RESPOND_SUB1 BIT(3) /* respond to Sub address 1 */ +#define PCA963X_MODE1_SLEEP BIT(4) /* put in low power mode */ +#define PCA963X_MODE1_AI_ROLL_PWM BIT(5) /* auto-increment only PWM's */ +#define PCA963X_MODE1_AI_ROLL_GRP BIT(6) /* AI only group-controls */ +#define PCA963X_MODE1_AI_EN BIT(7) /* enable Auto-Increment */ + +#define PCA963X_MODE2 0x01 /* mode 2 register addr */ +#define PCA963X_MODE2_OUTNE_OUTDRV BIT(0) /* outdrv determines LED state */ +#define PCA963X_MODE2_OUTNE_HIZ BIT(1) /* LED-state in Hi-Z */ +#define PCA963X_MODE2_OUTDRV_TOTEM_POLE BIT(2) /* outputs are totem-pole'd */ +#define PCA963X_MODE2_OCH_ACK BIT(3) /* out change on ACK else STOP */ +#define PCA963X_MODE2_INVRT BIT(4) /* output logic state inverted */ +#define PCA963X_MODE2_DMBLNK BIT(5) /* grp-ctrl blink else dimming */ + +#define PCA963X_PWM_ADDR(led) (0x02 + (led)) + +#define PCA9633_GRPPWM 0x06 /* group PWM duty cycle ctrl for PCA9633 */ +#define PCA9634_GRPPWM 0x0a /* group PWM duty cycle ctrl for PCA9634 */ +#define PCA9635_GRPPWM 0x12 /* group PWM duty cycle ctrl for PCA9635 */ +#define PCA9633_GRPFREQ 0x07 /* group frequency control for PCA9633 */ +#define PCA9634_GRPFREQ 0x0b /* group frequency control for PCA9634 */ +#define PCA9635_GRPFREQ 0x13 /* group frequency control for PCA9635 */ + +#define PCA9633_LEDOUT_BASE 0x08 /* LED output state 0 reg for PCA9633 */ +#define PCA9634_LEDOUT_BASE 0x0c /* LED output state 0 reg for PCA9635 */ +#define PCA9635_LEDOUT_BASE 0x14 /* LED output state 0 reg for PCA9634 */ +#define PCA963X_LEDOUT_ADDR(ledout_base, led_num) \ + ((ledout_base) + ((led_num) / 4)) + +#define PCA963X_LEDOUT_LED_OFF 0x0 /* LED off */ +#define PCA963X_LEDOUT_LED_ON 0x1 /* LED on */ +#define PCA963X_LEDOUT_LED_PWM 0x2 /* LED PWM mode */ +#define PCA963X_LEDOUT_LED_GRP_PWM 0x3 /* LED PWM + group PWM mode */ +#define PCA963X_LEDOUT_MASK GENMASK(1, 0) + +#define PCA963X_LEDOUT_LDR(drive, led_num) \ + (((drive) & PCA963X_LEDOUT_MASK) << (((led_num) % 4) << 1)) +#define PCA963X_LEDOUT_LDR_INV(drive, led_num) \ + (((drive) >> (((led_num) % 4) << 1)) & PCA963X_LEDOUT_MASK) + +#define PCA9633_SUBADDR(x) \ + ((((x) - 1) % 0x3) + 0x09) /* I2C subaddr for PCA9633 */ +#define PCA9634_SUBADDR(x) \ + ((((x) - 1) % 0x3) + 0x0e) /* I2C subaddr for PCA9634 */ +#define PCA9635_SUBADDR(x) \ + ((((x) - 1) % 0x3) + 0x18) /* I2C subaddr for PCA9635 */ +#define PCA963X_SUBADDR_SET(x) (((x) << 1) & 0xfe) + +#define PCA9633_ALLCALLADDR 0x0c /* I2C Led all call address for PCA9633 */ +#define PCA9634_ALLCALLADDR 0x11 /* I2C Led all call address for PCA9634 */ +#define PCA9635_ALLCALLADDR 0x1b /* I2C Led all call address for PCA9635 */ +#define PCA963X_ALLCALLADDR_SET(x) (((x) << 1) & 0xfe) + +/* Software reset password */ +#define PCA963X_PASSKEY1 0xa5 +#define PCA963X_PASSKEY2 0x5a enum pca963x_type { pca9633, @@ -65,21 +116,21 @@ struct pca963x_chipdef { static struct pca963x_chipdef pca963x_chipdefs[] = { [pca9633] = { - .grppwm = 0x6, - .grpfreq = 0x7, - .ledout_base = 0x8, + .grppwm = PCA9633_GRPPWM, + .grpfreq = PCA9633_GRPFREQ, + .ledout_base = PCA9633_LEDOUT_BASE, .n_leds = 4, }, [pca9634] = { - .grppwm = 0xa, - .grpfreq = 0xb, - .ledout_base = 0xc, + .grppwm = PCA9634_GRPPWM, + .grpfreq = PCA9634_GRPFREQ, + .ledout_base = PCA9634_LEDOUT_BASE, .n_leds = 8, }, [pca9635] = { - .grppwm = 0x12, - .grpfreq = 0x13, - .ledout_base = 0x14, + .grppwm = PCA9635_GRPPWM, + .grpfreq = PCA9635_GRPFREQ, + .ledout_base = PCA9635_LEDOUT_BASE, .n_leds = 16, }, }; @@ -120,7 +171,7 @@ struct pca963x_led { struct pca963x *chip; struct led_classdev led_cdev; int led_num; /* 0 .. 15 potentially */ - char name[32]; + char name[PCA963X_MAX_NAME_LEN]; u8 gdc; u8 gfrq; }; @@ -128,48 +179,44 @@ struct pca963x_led { static int pca963x_brightness(struct pca963x_led *pca963x, enum led_brightness brightness) { - u8 ledout_addr = pca963x->chip->chipdef->ledout_base - + (pca963x->led_num / 4); + u8 ledout_addr; u8 ledout; - int shift = 2 * (pca963x->led_num % 4); - u8 mask = 0x3 << shift; int ret; + ledout_addr = PCA963X_LEDOUT_ADDR(pca963x->chip->chipdef->ledout_base, + pca963x->led_num); ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr); + ledout &= ~PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_MASK, pca963x->led_num); switch (brightness) { case LED_FULL: - ret = i2c_smbus_write_byte_data(pca963x->chip->client, - ledout_addr, - (ledout & ~mask) | (PCA963X_LED_ON << shift)); + ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_ON, + pca963x->led_num); break; case LED_OFF: - ret = i2c_smbus_write_byte_data(pca963x->chip->client, - ledout_addr, ledout & ~mask); + ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_OFF, + pca963x->led_num); break; default: ret = i2c_smbus_write_byte_data(pca963x->chip->client, - PCA963X_PWM_BASE + pca963x->led_num, + PCA963X_PWM_ADDR(pca963x->led_num), brightness); if (ret < 0) return ret; - ret = i2c_smbus_write_byte_data(pca963x->chip->client, - ledout_addr, - (ledout & ~mask) | (PCA963X_LED_PWM << shift)); + ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_PWM, + pca963x->led_num); break; } - return ret; + return i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, + ledout); } static void pca963x_blink(struct pca963x_led *pca963x) { - u8 ledout_addr = pca963x->chip->chipdef->ledout_base + - (pca963x->led_num / 4); + u8 ledout_addr; u8 ledout; u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, PCA963X_MODE2); - int shift = 2 * (pca963x->led_num % 4); - u8 mask = 0x3 << shift; i2c_smbus_write_byte_data(pca963x->chip->client, pca963x->chip->chipdef->grppwm, pca963x->gdc); @@ -181,11 +228,17 @@ static void pca963x_blink(struct pca963x_led *pca963x) i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, mode2 | PCA963X_MODE2_DMBLNK); + ledout_addr = PCA963X_LEDOUT_ADDR(pca963x->chip->chipdef->ledout_base, + pca963x->led_num); mutex_lock(&pca963x->chip->mutex); ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr); - if ((ledout & mask) != (PCA963X_LED_GRP_PWM << shift)) + if (PCA963X_LEDOUT_LDR_INV(ledout, pca963x->led_num) != + PCA963X_LEDOUT_LED_GRP_PWM) { + ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_GRP_PWM, + pca963x->led_num); i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, - (ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift)); + ledout); + } mutex_unlock(&pca963x->chip->mutex); } @@ -201,7 +254,7 @@ static int pca963x_power_state(struct pca963x_led *pca963x) if (!(*leds_on) != !cached_leds) return i2c_smbus_write_byte_data(pca963x->chip->client, - PCA963X_MODE1, *leds_on ? 0 : BIT(4)); + PCA963X_MODE1, *leds_on ? 0 : PCA963X_MODE1_SLEEP); return 0; } @@ -428,7 +481,8 @@ static int pca963x_probe(struct i2c_client *client, /* Turn off LEDs by default*/ for (i = 0; i < chip->n_leds / 4; i++) - i2c_smbus_write_byte_data(client, chip->ledout_base + i, 0x00); + i2c_smbus_write_byte_data(client, chip->ledout_base + i, + PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_OFF, i)); for (i = 0; i < chip->n_leds; i++) { pca963x[i].led_num = i; @@ -462,19 +516,20 @@ static int pca963x_probe(struct i2c_client *client, } /* Disable LED all-call address, and power down initially */ - i2c_smbus_write_byte_data(client, PCA963X_MODE1, BIT(4)); + i2c_smbus_write_byte_data(client, PCA963X_MODE1, PCA963X_MODE1_SLEEP); if (pdata) { u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, PCA963X_MODE2); /* Configure output: open-drain or totem pole (push-pull) */ if (pdata->outdrv == PCA963X_OPEN_DRAIN) - mode2 |= 0x01; + mode2 |= PCA963X_MODE2_OUTNE_OUTDRV; else - mode2 |= 0x05; + mode2 |= PCA963X_MODE2_OUTNE_OUTDRV | + PCA963X_MODE2_OUTDRV_TOTEM_POLE; /* Configure direction: normal or inverted */ if (pdata->dir == PCA963X_INVERTED) - mode2 |= 0x10; + mode2 |= PCA963X_MODE2_INVRT; i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, mode2); } From 4121ef1608d73144da4b0cb002f4d7a942c25304 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 4 Oct 2017 13:30:56 +0200 Subject: [PATCH 08/57] leds: pca963x: save mode when setting power_state Currently, we blast sleep mode or 0 into the mode1 register, without taking notice of whatever was in it before. Lets be a bit more proper here and read the register and modify only the bit which we are concerned. Signed-off-by: Olliver Schinagl --- drivers/leds/leds-pca963x.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index e420a270a3c985..559332cc4ec861 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -252,9 +252,17 @@ static int pca963x_power_state(struct pca963x_led *pca963x) else clear_bit(pca963x->led_num, leds_on); - if (!(*leds_on) != !cached_leds) + if (!(*leds_on) != !cached_leds) { + u8 mode1 = i2c_smbus_read_byte_data(pca963x->chip->client, + PCA963X_MODE1); + + if (*leds_on) + mode1 &= ~PCA963X_MODE1_SLEEP; + else + mode1 |= PCA963X_MODE1_SLEEP; return i2c_smbus_write_byte_data(pca963x->chip->client, - PCA963X_MODE1, *leds_on ? 0 : PCA963X_MODE1_SLEEP); + PCA963X_MODE1, mode1); + } return 0; } From 10fd37634269a4044f3964eccb2c54f3ab2a3d7b Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 4 Oct 2017 13:58:30 +0200 Subject: [PATCH 09/57] leds: pca963x: refactor initial led output a little Let's improve readability a little bit by reordering initial LED output configuration. Signed-off-by: Olliver Schinagl --- drivers/leds/leds-pca963x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 559332cc4ec861..ba7cea1dd3db0c 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -529,12 +529,12 @@ static int pca963x_probe(struct i2c_client *client, if (pdata) { u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, PCA963X_MODE2); + + /* Always enable LED output */ + mode2 |= PCA963X_MODE2_OUTNE_OUTDRV; /* Configure output: open-drain or totem pole (push-pull) */ - if (pdata->outdrv == PCA963X_OPEN_DRAIN) - mode2 |= PCA963X_MODE2_OUTNE_OUTDRV; - else - mode2 |= PCA963X_MODE2_OUTNE_OUTDRV | - PCA963X_MODE2_OUTDRV_TOTEM_POLE; + if (pdata->outdrv == PCA963X_TOTEM_POLE) + mode2 |= PCA963X_MODE2_OUTDRV_TOTEM_POLE; /* Configure direction: normal or inverted */ if (pdata->dir == PCA963X_INVERTED) mode2 |= PCA963X_MODE2_INVRT; From 1568dc6d13d0fce5351744a0747260778c444edd Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 5 Apr 2016 14:03:14 +0200 Subject: [PATCH 10/57] leds: pca963x: remove whitespace and checkpatch problems This patch does some whitespace fixing to make the entire driver more consistent, especially with regards to alignment. Because of this it also reduces quite some of the checkpatch warnings. This did came at the cost of some minor 80 char warnings however to satisfy the alignment and improve readability. This patch does not introduce any binary changes. Signed-off-by: Olliver Schinagl --- drivers/leds/leds-pca963x.c | 58 ++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index ba7cea1dd3db0c..93f9a6f741b6f1 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -144,7 +144,7 @@ static const struct i2c_device_id pca963x_id[] = { { "pca9633", pca9633 }, { "pca9634", pca9634 }, { "pca9635", pca9635 }, - { } + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca963x_id); @@ -153,7 +153,7 @@ static const struct acpi_device_id pca963x_acpi_ids[] = { { "PCA9633", pca9633 }, { "PCA9634", pca9634 }, { "PCA9635", pca9635 }, - { } + { /* sentinel */ } }; MODULE_DEVICE_TABLE(acpi, pca963x_acpi_ids); @@ -161,7 +161,7 @@ struct pca963x_led; struct pca963x { struct pca963x_chipdef *chipdef; - struct mutex mutex; + struct mutex mutex; /* lock around parallel i2c access */ struct i2c_client *client; struct pca963x_led *leds; unsigned long leds_on; @@ -177,7 +177,7 @@ struct pca963x_led { }; static int pca963x_brightness(struct pca963x_led *pca963x, - enum led_brightness brightness) + enum led_brightness brightness) { u8 ledout_addr; u8 ledout; @@ -198,8 +198,8 @@ static int pca963x_brightness(struct pca963x_led *pca963x, break; default: ret = i2c_smbus_write_byte_data(pca963x->chip->client, - PCA963X_PWM_ADDR(pca963x->led_num), - brightness); + PCA963X_PWM_ADDR(pca963x->led_num), + brightness); if (ret < 0) return ret; ledout |= PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_PWM, @@ -215,18 +215,20 @@ static void pca963x_blink(struct pca963x_led *pca963x) { u8 ledout_addr; u8 ledout; - u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, - PCA963X_MODE2); + u8 mode2; i2c_smbus_write_byte_data(pca963x->chip->client, - pca963x->chip->chipdef->grppwm, pca963x->gdc); + pca963x->chip->chipdef->grppwm, + pca963x->gdc); i2c_smbus_write_byte_data(pca963x->chip->client, - pca963x->chip->chipdef->grpfreq, pca963x->gfrq); + pca963x->chip->chipdef->grpfreq, + pca963x->gfrq); + mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, PCA963X_MODE2); if (!(mode2 & PCA963X_MODE2_DMBLNK)) i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, - mode2 | PCA963X_MODE2_DMBLNK); + mode2 | PCA963X_MODE2_DMBLNK); ledout_addr = PCA963X_LEDOUT_ADDR(pca963x->chip->chipdef->ledout_base, pca963x->led_num); @@ -261,14 +263,14 @@ static int pca963x_power_state(struct pca963x_led *pca963x) else mode1 |= PCA963X_MODE1_SLEEP; return i2c_smbus_write_byte_data(pca963x->chip->client, - PCA963X_MODE1, mode1); + PCA963X_MODE1, mode1); } return 0; } static int pca963x_led_set(struct led_classdev *led_cdev, - enum led_brightness value) + enum led_brightness value) { struct pca963x_led *pca963x; int ret; @@ -288,7 +290,7 @@ static int pca963x_led_set(struct led_classdev *led_cdev, } static unsigned int pca963x_period_scale(struct pca963x_led *pca963x, - unsigned int val) + unsigned int val) { unsigned int scaling = pca963x->chip->chipdef->scaling; @@ -296,7 +298,8 @@ static unsigned int pca963x_period_scale(struct pca963x_led *pca963x, } static int pca963x_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, unsigned long *delay_off) + unsigned long *delay_on, + unsigned long *delay_off) { struct pca963x_led *pca963x; unsigned long time_on, time_off, period; @@ -315,7 +318,7 @@ static int pca963x_blink_set(struct led_classdev *led_cdev, period = pca963x_period_scale(pca963x, time_on + time_off); - /* If period not supported by hardware, default to someting sane. */ + /* If period not supported by hardware, default to something sane. */ if ((period < PCA963X_BLINK_PERIOD_MIN) || (period > PCA963X_BLINK_PERIOD_MAX)) { time_on = 500; @@ -374,10 +377,8 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) res = of_property_read_u32(child, "reg", ®); if ((res != 0) || (reg >= chip->n_leds)) continue; - led.name = - of_get_property(child, "label", NULL) ? : child->name; - led.default_trigger = - of_get_property(child, "linux,default-trigger", NULL); + led.name = of_get_property(child, "label", NULL) ? : child->name; + led.default_trigger = of_get_property(child, "linux,default-trigger", NULL); pca963x_leds[reg] = led; } pdata = devm_kzalloc(&client->dev, @@ -429,7 +430,7 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) #endif static int pca963x_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct pca963x *pca963x_chip; struct pca963x_led *pca963x; @@ -458,18 +459,15 @@ static int pca963x_probe(struct i2c_client *client, } if (pdata && (pdata->leds.num_leds < 1 || - pdata->leds.num_leds > chip->n_leds)) { - dev_err(&client->dev, "board info must claim 1-%d LEDs", - chip->n_leds); + pdata->leds.num_leds > chip->n_leds)) { + dev_err(&client->dev, "board info must claim 1-%d LEDs", chip->n_leds); return -EINVAL; } - pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip), - GFP_KERNEL); + pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip), GFP_KERNEL); if (!pca963x_chip) return -ENOMEM; - pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x), - GFP_KERNEL); + pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x), GFP_KERNEL); if (!pca963x) return -ENOMEM; @@ -490,7 +488,7 @@ static int pca963x_probe(struct i2c_client *client, /* Turn off LEDs by default*/ for (i = 0; i < chip->n_leds / 4; i++) i2c_smbus_write_byte_data(client, chip->ledout_base + i, - PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_OFF, i)); + PCA963X_LEDOUT_LDR(PCA963X_LEDOUT_LED_OFF, i)); for (i = 0; i < chip->n_leds; i++) { pca963x[i].led_num = i; @@ -507,7 +505,7 @@ static int pca963x_probe(struct i2c_client *client, pdata->leds.leds[i].default_trigger; } if (!pdata || i >= pdata->leds.num_leds || - !pdata->leds.leds[i].name) + !pdata->leds.leds[i].name) snprintf(pca963x[i].name, sizeof(pca963x[i].name), "pca963x:%d:%.2x:%d", client->adapter->nr, client->addr, i); From acf0551681346af3ba7ff487d79296d0b07c6678 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Fri, 17 Mar 2017 23:00:27 +0100 Subject: [PATCH 11/57] leds: pca963x: set DMBLNK as default during probe The PCA963x series of chips have several operating modes controlled via the LEDOUT register. LDR controls whether the leds are completely off (0x0), completely on (0x1), only controlled via the PWM registers (0x2) or a special 'grouped' mode (0x3). In the grouped mode, the leds can either blink, or PWM in synchronization. Since the LED framework currently does not support linked LEDs and thus we cannot PWM in synchronization, set the default group to blink, to ensure that hardware based blinking at least is synchronized when blinking. Signed-off-by: Olliver Schinagl --- drivers/leds/leds-pca963x.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index 93f9a6f741b6f1..49cb9ba633a80f 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -215,7 +215,6 @@ static void pca963x_blink(struct pca963x_led *pca963x) { u8 ledout_addr; u8 ledout; - u8 mode2; i2c_smbus_write_byte_data(pca963x->chip->client, pca963x->chip->chipdef->grppwm, @@ -225,11 +224,6 @@ static void pca963x_blink(struct pca963x_led *pca963x) pca963x->chip->chipdef->grpfreq, pca963x->gfrq); - mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, PCA963X_MODE2); - if (!(mode2 & PCA963X_MODE2_DMBLNK)) - i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, - mode2 | PCA963X_MODE2_DMBLNK); - ledout_addr = PCA963X_LEDOUT_ADDR(pca963x->chip->chipdef->ledout_base, pca963x->led_num); mutex_lock(&pca963x->chip->mutex); @@ -528,8 +522,8 @@ static int pca963x_probe(struct i2c_client *client, u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, PCA963X_MODE2); - /* Always enable LED output */ - mode2 |= PCA963X_MODE2_OUTNE_OUTDRV; + /* Always enable LED output and group blink mode */ + mode2 |= PCA963X_MODE2_OUTNE_OUTDRV | PCA963X_MODE2_DMBLNK; /* Configure output: open-drain or totem pole (push-pull) */ if (pdata->outdrv == PCA963X_TOTEM_POLE) mode2 |= PCA963X_MODE2_OUTDRV_TOTEM_POLE; From ed038f455410cb24043cdba6a3fe928c61522443 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 3 Jan 2017 09:31:59 +0100 Subject: [PATCH 12/57] mfd: axp20x: fixup includes Add the bitops.h header as we need it, alphabetize header order. Signed-off-by: Olliver Schinagl --- drivers/mfd/axp20x.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 2468b431bb2203..5eb4cf3bf51a53 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -16,18 +16,19 @@ * published by the Free Software Foundation. */ -#include +#include +#include #include +#include #include #include +#include +#include #include +#include #include #include #include -#include -#include -#include -#include #define AXP20X_OFF 0x80 From 4a4efcfcc88f8658c065a657bfdf4bff106ba7b5 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 4 Oct 2017 15:33:23 +0200 Subject: [PATCH 13/57] mfd: axp20x: use explicit bit defines The AXP20X_OFF define is an actual specific bit, define it as such. Signed-off-by: Olliver Schinagl --- drivers/mfd/axp20x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 5eb4cf3bf51a53..9205e8eebd5148 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -30,7 +30,7 @@ #include #include -#define AXP20X_OFF 0x80 +#define AXP20X_OFF BIT(7) #define AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE 0 #define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE BIT(4) From 03ce04d76b85c3a326116a5072d1cd34ac9333d0 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 3 Jan 2017 09:37:37 +0100 Subject: [PATCH 14/57] power: suppy: axp20x: add missing include bitops.h The axp20x_usb_power driver uses BIT() operations but lacks the include for it. Include the bitops.h header file. Signed-off-by: Olliver Schinagl --- drivers/power/supply/axp20x_usb_power.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 44f70dcea61e3c..56970eb4339ff1 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -10,6 +10,7 @@ * option) any later version. */ +#include #include #include #include From dd35aa49582f77bba1f2119bb4a2dec136c6b422 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 3 Jan 2017 09:38:33 +0100 Subject: [PATCH 15/57] power: suppy: axp288: use the BIT() macro Make use of the recommended BIT() macro for bit defines. Signed-off-by: Olliver Schinagl --- drivers/power/supply/axp288_charger.c | 35 ++++++++++++++------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index d51ebd1da65e77..2fd48ad9fdfcc5 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -28,17 +29,17 @@ #include #include -#define PS_STAT_VBUS_TRIGGER (1 << 0) -#define PS_STAT_BAT_CHRG_DIR (1 << 2) -#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3) -#define PS_STAT_VBUS_VALID (1 << 4) -#define PS_STAT_VBUS_PRESENT (1 << 5) +#define PS_STAT_VBUS_TRIGGER BIT(0) +#define PS_STAT_BAT_CHRG_DIR BIT(2) +#define PS_STAT_VBAT_ABOVE_VHOLD BIT(3) +#define PS_STAT_VBUS_VALID BIT(4) +#define PS_STAT_VBUS_PRESENT BIT(5) -#define CHRG_STAT_BAT_SAFE_MODE (1 << 3) -#define CHRG_STAT_BAT_VALID (1 << 4) -#define CHRG_STAT_BAT_PRESENT (1 << 5) -#define CHRG_STAT_CHARGING (1 << 6) -#define CHRG_STAT_PMIC_OTP (1 << 7) +#define CHRG_STAT_BAT_SAFE_MODE BIT(3) +#define CHRG_STAT_BAT_VALID BIT(4) +#define CHRG_STAT_BAT_PRESENT BIT(5) +#define CHRG_STAT_CHARGING BIT(6) +#define CHRG_STAT_PMIC_OTP BIT(7) #define VBUS_ISPOUT_CUR_LIM_MASK 0x03 #define VBUS_ISPOUT_CUR_LIM_BIT_POS 0 @@ -51,33 +52,33 @@ #define VBUS_ISPOUT_VHOLD_SET_OFFSET 4000 /* 4000mV */ #define VBUS_ISPOUT_VHOLD_SET_LSB_RES 100 /* 100mV */ #define VBUS_ISPOUT_VHOLD_SET_4300MV 0x3 /* 4300mV */ -#define VBUS_ISPOUT_VBUS_PATH_DIS (1 << 7) +#define VBUS_ISPOUT_VBUS_PATH_DIS BIT(7) #define CHRG_CCCV_CC_MASK 0xf /* 4 bits */ #define CHRG_CCCV_CC_BIT_POS 0 #define CHRG_CCCV_CC_OFFSET 200 /* 200mA */ #define CHRG_CCCV_CC_LSB_RES 200 /* 200mA */ -#define CHRG_CCCV_ITERM_20P (1 << 4) /* 20% of CC */ +#define CHRG_CCCV_ITERM_20P BIT(4) /* 20% of CC */ #define CHRG_CCCV_CV_MASK 0x60 /* 2 bits */ #define CHRG_CCCV_CV_BIT_POS 5 #define CHRG_CCCV_CV_4100MV 0x0 /* 4.10V */ #define CHRG_CCCV_CV_4150MV 0x1 /* 4.15V */ #define CHRG_CCCV_CV_4200MV 0x2 /* 4.20V */ #define CHRG_CCCV_CV_4350MV 0x3 /* 4.35V */ -#define CHRG_CCCV_CHG_EN (1 << 7) +#define CHRG_CCCV_CHG_EN BIT(7) #define CNTL2_CC_TIMEOUT_MASK 0x3 /* 2 bits */ #define CNTL2_CC_TIMEOUT_OFFSET 6 /* 6 Hrs */ #define CNTL2_CC_TIMEOUT_LSB_RES 2 /* 2 Hrs */ #define CNTL2_CC_TIMEOUT_12HRS 0x3 /* 12 Hrs */ -#define CNTL2_CHGLED_TYPEB (1 << 4) -#define CNTL2_CHG_OUT_TURNON (1 << 5) +#define CNTL2_CHGLED_TYPEB BIT(4) +#define CNTL2_CHG_OUT_TURNON BIT(5) #define CNTL2_PC_TIMEOUT_MASK 0xC0 #define CNTL2_PC_TIMEOUT_OFFSET 40 /* 40 mins */ #define CNTL2_PC_TIMEOUT_LSB_RES 10 /* 10 mins */ #define CNTL2_PC_TIMEOUT_70MINS 0x3 -#define CHRG_ILIM_TEMP_LOOP_EN (1 << 3) +#define CHRG_ILIM_TEMP_LOOP_EN BIT(3) #define CHRG_VBUS_ILIM_MASK 0xf0 #define CHRG_VBUS_ILIM_BIT_POS 4 #define CHRG_VBUS_ILIM_100MA 0x0 /* 100mA */ @@ -91,7 +92,7 @@ #define CHRG_VLTFC_0C 0xA5 /* 0 DegC */ #define CHRG_VHTFC_45C 0x1F /* 45 DegC */ -#define FG_CNTL_OCV_ADJ_EN (1 << 3) +#define FG_CNTL_OCV_ADJ_EN BIT(3) #define CV_4100MV 4100 /* 4100mV */ #define CV_4150MV 4150 /* 4150mV */ From 13b162f510027ef61a7612a8922e9b0429b4ff76 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 3 Jan 2017 09:36:06 +0100 Subject: [PATCH 16/57] regulator: axp20x: use defines for masks The AXP20X driver currently has several masks defined throughout the code. Use nice defines to make them clean and more descriptive. Additionally include bitops.h which was missing before. Signed-off-by: Olliver Schinagl --- drivers/regulator/axp20x-regulator.c | 735 ++++++++++++++++++++------- 1 file changed, 556 insertions(+), 179 deletions(-) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index e1761df4cbfddb..c5deb5dc4382a2 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -13,6 +13,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -24,20 +25,237 @@ #include #include +#define AXP20X_GPIO0_FUNC_MASK GENMASK(3, 0) +#define AXP20X_GPIO1_FUNC_MASK GENMASK(3, 0) + #define AXP20X_IO_ENABLED 0x03 #define AXP20X_IO_DISABLED 0x07 +#define AXP20X_WORKMODE_DCDC2_MASK BIT_MASK(2) +#define AXP20X_WORKMODE_DCDC3_MASK BIT_MASK(1) + +#define AXP20X_FREQ_DCDC_MASK GENMASK(3, 0) + +#define AXP20X_VBUS_IPSOUT_MGMT_MASK BIT_MASK(2) + +#define AXP20X_DCDC2_V_OUT_MASK GENMASK(5, 0) +#define AXP20X_DCDC3_V_OUT_MASK GENMASK(7, 0) +#define AXP20X_LDO24_V_OUT_MASK GENMASK(7, 4) +#define AXP20X_LDO3_V_OUT_MASK GENMASK(6, 0) +#define AXP20X_LDO5_V_OUT_MASK GENMASK(7, 4) + +#define AXP20X_PWR_OUT_EXTEN_MASK BIT_MASK(0) +#define AXP20X_PWR_OUT_DCDC3_MASK BIT_MASK(1) +#define AXP20X_PWR_OUT_LDO2_MASK BIT_MASK(2) +#define AXP20X_PWR_OUT_LDO4_MASK BIT_MASK(3) +#define AXP20X_PWR_OUT_DCDC2_MASK BIT_MASK(4) +#define AXP20X_PWR_OUT_LDO3_MASK BIT_MASK(6) + +#define AXP20X_LDO4_V_OUT_1250mV_START 0x0 +#define AXP20X_LDO4_V_OUT_1250mV_STEPS 0 +#define AXP20X_LDO4_V_OUT_1250mV_END \ + AXP20X_LDO4_V_OUT_1250mV_START + AXP20X_LDO4_V_OUT_1250mV_STEPS +#define AXP20X_LDO4_V_OUT_1300mV_START 0x1 +#define AXP20X_LDO4_V_OUT_1300mV_STEPS 7 +#define AXP20X_LDO4_V_OUT_1300mV_END \ + AXP20X_LDO4_V_OUT_1300mV_START + AXP20X_LDO4_V_OUT_1300mV_STEPS +#define AXP20X_LDO4_V_OUT_2500mV_START 0x9 +#define AXP20X_LDO4_V_OUT_2500mV_STEPS 0 +#define AXP20X_LDO4_V_OUT_2500mV_END \ + AXP20X_LDO4_V_OUT_2500mV_START + AXP20X_LDO4_V_OUT_2500mV_STEPS +#define AXP20X_LDO4_V_OUT_2700mV_START 0xa +#define AXP20X_LDO4_V_OUT_2700mV_STEPS 1 +#define AXP20X_LDO4_V_OUT_2700mV_END \ + AXP20X_LDO4_V_OUT_2700mV_START + AXP20X_LDO4_V_OUT_2700mV_STEPS +#define AXP20X_LDO4_V_OUT_3000mV_START 0xc +#define AXP20X_LDO4_V_OUT_3000mV_STEPS 3 +#define AXP20X_LDO4_V_OUT_3000mV_END \ + AXP20X_LDO4_V_OUT_3000mV_START + AXP20X_LDO4_V_OUT_3000mV_STEPS +#define AXP20X_LDO4_V_OUT_NUM_VOLTAGES 16 + #define AXP22X_IO_ENABLED 0x03 #define AXP22X_IO_DISABLED 0x04 -#define AXP20X_WORKMODE_DCDC2_MASK BIT(2) -#define AXP20X_WORKMODE_DCDC3_MASK BIT(1) -#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT(x) - -#define AXP20X_FREQ_DCDC_MASK 0x0f +#define AXP22X_WORKMODE_DCDCX_MASK(x) BIT_MASK(x) #define AXP22X_MISC_N_VBUSEN_FUNC BIT(4) +#define AXP22X_DCDC1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DCDC2_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC3_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC4_V_OUT_MASK GENMASK(5, 0) +#define AXP22X_DCDC5_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DC5LDO_V_OUT_MASK GENMASK(2, 0) +#define AXP22X_ALDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ALDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ALDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_DLDO4_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_ELDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_LDO_IO0_V_OUT_MASK GENMASK(4, 0) +#define AXP22X_LDO_IO1_V_OUT_MASK GENMASK(4, 0) + +#define AXP22X_PWR_OUT_DC5LDO_MASK BIT_MASK(0) +#define AXP22X_PWR_OUT_DCDC1_MASK BIT_MASK(1) +#define AXP22X_PWR_OUT_DCDC2_MASK BIT_MASK(2) +#define AXP22X_PWR_OUT_DCDC3_MASK BIT_MASK(3) +#define AXP22X_PWR_OUT_DCDC4_MASK BIT_MASK(4) +#define AXP22X_PWR_OUT_DCDC5_MASK BIT_MASK(5) +#define AXP22X_PWR_OUT_ALDO1_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_ALDO2_MASK BIT_MASK(7) + +#define AXP22X_PWR_OUT_SW_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_DC1SW_MASK BIT_MASK(7) + +#define AXP22X_PWR_OUT_ELDO1_MASK BIT_MASK(0) +#define AXP22X_PWR_OUT_ELDO2_MASK BIT_MASK(1) +#define AXP22X_PWR_OUT_ELDO3_MASK BIT_MASK(2) +#define AXP22X_PWR_OUT_DLDO1_MASK BIT_MASK(3) +#define AXP22X_PWR_OUT_DLDO2_MASK BIT_MASK(4) +#define AXP22X_PWR_OUT_DLDO3_MASK BIT_MASK(5) +#define AXP22X_PWR_OUT_DLDO4_MASK BIT_MASK(6) +#define AXP22X_PWR_OUT_ALDO3_MASK BIT_MASK(7) + +#define AXP803_PWR_OUT_DCDC1_MASK BIT_MASK(0) +#define AXP803_PWR_OUT_DCDC2_MASK BIT_MASK(1) +#define AXP803_PWR_OUT_DCDC3_MASK BIT_MASK(2) +#define AXP803_PWR_OUT_DCDC4_MASK BIT_MASK(3) +#define AXP803_PWR_OUT_DCDC5_MASK BIT_MASK(4) +#define AXP803_PWR_OUT_DCDC6_MASK BIT_MASK(5) + +#define AXP803_PWR_OUT_FLDO1_MASK BIT_MASK(2) +#define AXP803_PWR_OUT_FLDO2_MASK BIT_MASK(3) + +#define AXP803_DCDC1_V_OUT_MASK GENMASK(4, 0) +#define AXP803_DCDC2_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC3_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC4_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC5_V_OUT_MASK GENMASK(6, 0) +#define AXP803_DCDC6_V_OUT_MASK GENMASK(6, 0) + +#define AXP803_FLDO1_V_OUT_MASK GENMASK(3, 0) +#define AXP803_FLDO2_V_OUT_MASK GENMASK(3, 0) + +#define AXP803_DCDC23_POLYPHASE_DUAL BIT(6) +#define AXP803_DCDC56_POLYPHASE_DUAL BIT(5) + +#define AXP803_DCDC234_500mV_START 0x00 +#define AXP803_DCDC234_500mV_STEPS 70 +#define AXP803_DCDC234_500mV_END \ + AXP803_DCDC234_500mV_START + AXP803_DCDC234_500mV_STEPS +#define AXP803_DCDC234_1220mV_START 0x47 +#define AXP803_DCDC234_1220mV_STEPS 4 +#define AXP803_DCDC234_1220mV_END \ + AXP803_DCDC234_1220mV_START + AXP803_DCDC234_1220mV_STEPS +#define AXP803_DCDC234_NUM_VOLTAGES 76 + +#define AXP803_DCDC5_800mV_START 0x00 +#define AXP803_DCDC5_800mV_STEPS 32 +#define AXP803_DCDC5_800mV_END \ + AXP803_DCDC5_800mV_START + AXP803_DCDC5_800mV_STEPS +#define AXP803_DCDC5_1140mV_START 0x21 +#define AXP803_DCDC5_1140mV_STEPS 35 +#define AXP803_DCDC5_1140mV_END \ + AXP803_DCDC5_1140mV_START + AXP803_DCDC5_1140mV_STEPS +#define AXP803_DCDC5_NUM_VOLTAGES 68 + +#define AXP803_DCDC6_600mV_START 0x00 +#define AXP803_DCDC6_600mV_STEPS 50 +#define AXP803_DCDC6_600mV_END \ + AXP803_DCDC6_600mV_START + AXP803_DCDC6_600mV_STEPS +#define AXP803_DCDC6_1120mV_START 0x33 +#define AXP803_DCDC6_1120mV_STEPS 14 +#define AXP803_DCDC6_1120mV_END \ + AXP803_DCDC6_1120mV_START + AXP803_DCDC6_1120mV_STEPS +#define AXP803_DCDC6_NUM_VOLTAGES 72 + +#define AXP803_DLDO2_700mV_START 0x00 +#define AXP803_DLDO2_700mV_STEPS 26 +#define AXP803_DLDO2_700mV_END \ + AXP803_DLDO2_700mV_START + AXP803_DLDO2_700mV_STEPS +#define AXP803_DLDO2_3400mV_START 0x1b +#define AXP803_DLDO2_3400mV_STEPS 4 +#define AXP803_DLDO2_3400mV_END \ + AXP803_DLDO2_3400mV_START + AXP803_DLDO2_3400mV_STEPS +#define AXP803_DLDO2_NUM_VOLTAGES 32 + +#define AXP806_DCDCA_V_CTRL_MASK GENMASK(6, 0) +#define AXP806_DCDCB_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_DCDCC_V_CTRL_MASK GENMASK(6, 0) +#define AXP806_DCDCD_V_CTRL_MASK GENMASK(5, 0) +#define AXP806_DCDCE_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO1_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO2_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_ALDO3_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_BLDO1_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO2_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO3_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_BLDO4_V_CTRL_MASK GENMASK(3, 0) +#define AXP806_CLDO1_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_CLDO2_V_CTRL_MASK GENMASK(4, 0) +#define AXP806_CLDO3_V_CTRL_MASK GENMASK(4, 0) + +#define AXP806_PWR_OUT_DCDCA_MASK BIT_MASK(0) +#define AXP806_PWR_OUT_DCDCB_MASK BIT_MASK(1) +#define AXP806_PWR_OUT_DCDCC_MASK BIT_MASK(2) +#define AXP806_PWR_OUT_DCDCD_MASK BIT_MASK(3) +#define AXP806_PWR_OUT_DCDCE_MASK BIT_MASK(4) +#define AXP806_PWR_OUT_ALDO1_MASK BIT_MASK(5) +#define AXP806_PWR_OUT_ALDO2_MASK BIT_MASK(6) +#define AXP806_PWR_OUT_ALDO3_MASK BIT_MASK(7) +#define AXP806_PWR_OUT_BLDO1_MASK BIT_MASK(0) +#define AXP806_PWR_OUT_BLDO2_MASK BIT_MASK(1) +#define AXP806_PWR_OUT_BLDO3_MASK BIT_MASK(2) +#define AXP806_PWR_OUT_BLDO4_MASK BIT_MASK(3) +#define AXP806_PWR_OUT_CLDO1_MASK BIT_MASK(4) +#define AXP806_PWR_OUT_CLDO2_MASK BIT_MASK(5) +#define AXP806_PWR_OUT_CLDO3_MASK BIT_MASK(6) +#define AXP806_PWR_OUT_SW_MASK BIT_MASK(7) + +#define AXP806_DCDCAB_POLYPHASE_DUAL 0x40 +#define AXP806_DCDCABC_POLYPHASE_TRI 0x80 +#define AXP806_DCDCABC_POLYPHASE_MASK GENMASK(7, 6) + +#define AXP806_DCDCDE_POLYPHASE_DUAL BIT(5) + +#define AXP806_DCDCA_600mV_START 0x00 +#define AXP806_DCDCA_600mV_STEPS 50 +#define AXP806_DCDCA_600mV_END \ + AXP806_DCDCA_600mV_START + AXP806_DCDCA_600mV_STEPS +#define AXP806_DCDCA_1120mV_START 0x33 +#define AXP806_DCDCA_1120mV_STEPS 14 +#define AXP806_DCDCA_1120mV_END \ + AXP806_DCDCA_1120mV_START + AXP806_DCDCA_1120mV_STEPS +#define AXP806_DCDCA_NUM_VOLTAGES 72 + +#define AXP806_DCDCD_600mV_START 0x00 +#define AXP806_DCDCD_600mV_STEPS 45 +#define AXP806_DCDCD_600mV_END \ + AXP806_DCDCD_600mV_START + AXP806_DCDCD_600mV_STEPS +#define AXP806_DCDCD_1600mV_START 0x2e +#define AXP806_DCDCD_1600mV_STEPS 17 +#define AXP806_DCDCD_1600mV_END \ + AXP806_DCDCD_1600mV_START + AXP806_DCDCD_1600mV_STEPS +#define AXP806_DCDCD_NUM_VOLTAGES 64 + +#define AXP809_DCDC4_600mV_START 0x00 +#define AXP809_DCDC4_600mV_STEPS 47 +#define AXP809_DCDC4_600mV_END \ + AXP809_DCDC4_600mV_START + AXP809_DCDC4_600mV_STEPS +#define AXP809_DCDC4_1800mV_START 0x30 +#define AXP809_DCDC4_1800mV_STEPS 8 +#define AXP809_DCDC4_1800mV_END \ + AXP809_DCDC4_1800mV_START + AXP809_DCDC4_1800mV_STEPS +#define AXP809_DCDC4_NUM_VOLTAGES 57 + +#define AXP813_DCDC7_V_OUT_MASK GENMASK(6, 0) + +#define AXP813_PWR_OUT_DCDC7_MASK BIT_MASK(6) + #define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg, \ _vmask, _ereg, _emask, _enable_val, _disable_val) \ [_family##_##_id] = { \ @@ -157,77 +375,116 @@ static const struct regulator_ops axp20x_ops_sw = { }; static const struct regulator_linear_range axp20x_ldo4_ranges[] = { - REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0), - REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000), - REGULATOR_LINEAR_RANGE(2500000, 0x9, 0x9, 0), - REGULATOR_LINEAR_RANGE(2700000, 0xa, 0xb, 100000), - REGULATOR_LINEAR_RANGE(3000000, 0xc, 0xf, 100000), + REGULATOR_LINEAR_RANGE(1250000, + AXP20X_LDO4_V_OUT_1250mV_START, + AXP20X_LDO4_V_OUT_1250mV_END, + 0), + REGULATOR_LINEAR_RANGE(1300000, + AXP20X_LDO4_V_OUT_1300mV_START, + AXP20X_LDO4_V_OUT_1300mV_END, + 100000), + REGULATOR_LINEAR_RANGE(2500000, + AXP20X_LDO4_V_OUT_2500mV_START, + AXP20X_LDO4_V_OUT_2500mV_END, + 0), + REGULATOR_LINEAR_RANGE(2700000, + AXP20X_LDO4_V_OUT_2700mV_START, + AXP20X_LDO4_V_OUT_2700mV_END, + 100000), + REGULATOR_LINEAR_RANGE(3000000, + AXP20X_LDO4_V_OUT_3000mV_START, + AXP20X_LDO4_V_OUT_3000mV_END, + 100000), }; static const struct regulator_desc axp20x_regulators[] = { AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25, - AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10), + AXP20X_DCDC2_V_OUT, AXP20X_DCDC2_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25, - AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02), + AXP20X_DCDC3_V_OUT, AXP20X_DCDC3_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC3_MASK), AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300), AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100, - AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04), + AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO2_MASK), AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25, - AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40), - AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_ranges, - 16, AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, - 0x08), + AXP20X_LDO3_V_OUT, AXP20X_LDO3_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO3_MASK), + AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", + axp20x_ldo4_ranges, AXP20X_LDO4_V_OUT_NUM_VOLTAGES, + AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK, + AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO4_MASK), AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100, - AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07, + AXP20X_LDO5_V_OUT, AXP20X_LDO5_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP20X_IO_ENABLED, AXP20X_IO_DISABLED), }; static const struct regulator_desc axp22x_regulators[] = { AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), + AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK), AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20, - AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)), + AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20, - AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), + AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK), AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20, - AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(4)), + AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK), AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, - AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP22X, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, - AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), + AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK), AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP22X_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)), + AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), /* Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000), }; @@ -240,240 +497,354 @@ static const struct regulator_desc axp22x_drivevbus_regulator = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .enable_reg = AXP20X_VBUS_IPSOUT_MGMT, - .enable_mask = BIT(2), + .enable_mask = AXP20X_VBUS_IPSOUT_MGMT_MASK, .ops = &axp20x_ops_sw, }; /* DCDC ranges shared with AXP813 */ static const struct regulator_linear_range axp803_dcdc234_ranges[] = { - REGULATOR_LINEAR_RANGE(500000, 0x0, 0x46, 10000), - REGULATOR_LINEAR_RANGE(1220000, 0x47, 0x4b, 20000), + REGULATOR_LINEAR_RANGE(500000, + AXP803_DCDC234_500mV_START, + AXP803_DCDC234_500mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1220000, + AXP803_DCDC234_1220mV_START, + AXP803_DCDC234_1220mV_END, + 20000), }; static const struct regulator_linear_range axp803_dcdc5_ranges[] = { - REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 10000), - REGULATOR_LINEAR_RANGE(1140000, 0x21, 0x44, 20000), + REGULATOR_LINEAR_RANGE(800000, + AXP803_DCDC5_800mV_START, + AXP803_DCDC5_800mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1140000, + AXP803_DCDC5_1140mV_START, + AXP803_DCDC5_1140mV_END, + 20000), }; static const struct regulator_linear_range axp803_dcdc6_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), - REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), + REGULATOR_LINEAR_RANGE(600000, + AXP803_DCDC6_600mV_START, + AXP803_DCDC6_600mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1120000, + AXP803_DCDC6_1120mV_START, + AXP803_DCDC6_1120mV_END, + 20000), }; -/* AXP806's CLDO2 and AXP809's DLDO1 shares the same range */ +/* AXP806's CLDO2 and AXP809's DLDO1 share the same range */ static const struct regulator_linear_range axp803_dldo2_ranges[] = { - REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000), - REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000), + REGULATOR_LINEAR_RANGE(700000, + AXP803_DLDO2_700mV_START, + AXP803_DLDO2_700mV_END, + 100000), + REGULATOR_LINEAR_RANGE(3400000, + AXP803_DLDO2_3400mV_START, + AXP803_DLDO2_3400mV_END, + 200000), }; static const struct regulator_desc axp803_regulators[] = { AXP_DESC(AXP803, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)), - AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges, - 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(1)), - AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges, - 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges, - 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(3)), - AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges, - 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), - AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges, - 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(5)), + AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK), + AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK), + AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK), + AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", + axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES, + AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK), + AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), AXP_DESC(AXP803, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP803, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP803, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP803, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), - AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(4)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), + AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP803, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP803, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP803, ELDO1, "eldo1", "eldoin", 700, 1900, 50, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP803, ELDO2, "eldo2", "eldoin", 700, 1900, 50, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP803, ELDO3, "eldo3", "eldoin", 700, 1900, 50, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), AXP_DESC(AXP803, FLDO1, "fldo1", "fldoin", 700, 1450, 50, - AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)), + AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK), AXP_DESC(AXP803, FLDO2, "fldo2", "fldoin", 700, 1450, 50, - AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)), + AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK), AXP_DESC_IO(AXP803, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_IO(AXP803, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP803, RTC_LDO, "rtc-ldo", "ips", 3000), }; static const struct regulator_linear_range axp806_dcdca_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), - REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), + REGULATOR_LINEAR_RANGE(600000, + AXP806_DCDCA_600mV_START, + AXP806_DCDCA_600mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1120000, + AXP806_DCDCA_1120mV_START, + AXP806_DCDCA_1120mV_END, + 20000), }; static const struct regulator_linear_range axp806_dcdcd_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2d, 20000), - REGULATOR_LINEAR_RANGE(1600000, 0x2e, 0x3f, 100000), + REGULATOR_LINEAR_RANGE(600000, + AXP806_DCDCD_600mV_START, + AXP806_DCDCD_600mV_END, + 20000), + REGULATOR_LINEAR_RANGE(1600000, + AXP806_DCDCD_600mV_START, + AXP806_DCDCD_600mV_END, + 100000), }; static const struct regulator_desc axp806_regulators[] = { - AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", axp806_dcdca_ranges, - 72, AXP806_DCDCA_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, - BIT(0)), + AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", + axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES, + AXP806_DCDCA_V_CTRL, AXP806_DCDCA_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCA_MASK), AXP_DESC(AXP806, DCDCB, "dcdcb", "vinb", 1000, 2550, 50, - AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(1)), - AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", axp806_dcdca_ranges, - 72, AXP806_DCDCC_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", axp806_dcdcd_ranges, - 64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1, - BIT(3)), + AXP806_DCDCB_V_CTRL, AXP806_DCDCB_V_CTRL, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCB_MASK), + AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", + axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES, + AXP806_DCDCC_V_CTRL, AXP806_DCDCC_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCC_MASK), + AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", + axp806_dcdcd_ranges, AXP806_DCDCD_NUM_VOLTAGES, + AXP806_DCDCD_V_CTRL, AXP806_DCDCD_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCD_MASK), AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100, - AXP806_DCDCE_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)), + AXP806_DCDCE_V_CTRL, AXP806_DCDCE_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCE_MASK), AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)), + AXP806_ALDO1_V_CTRL, AXP806_ALDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100, - AXP806_ALDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(6)), + AXP806_ALDO2_V_CTRL, AXP806_ALDO2_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP806, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP806_ALDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(7)), + AXP806_ALDO3_V_CTRL, AXP806_ALDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP806, BLDO1, "bldo1", "bldoin", 700, 1900, 100, - AXP806_BLDO1_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(0)), + AXP806_BLDO1_V_CTRL, AXP806_BLDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO1_MASK), AXP_DESC(AXP806, BLDO2, "bldo2", "bldoin", 700, 1900, 100, - AXP806_BLDO2_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(1)), + AXP806_BLDO2_V_CTRL, AXP806_BLDO2_V_CTRL, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO2_MASK), AXP_DESC(AXP806, BLDO3, "bldo3", "bldoin", 700, 1900, 100, - AXP806_BLDO3_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(2)), + AXP806_BLDO3_V_CTRL, AXP806_BLDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO3_MASK), AXP_DESC(AXP806, BLDO4, "bldo4", "bldoin", 700, 1900, 100, - AXP806_BLDO4_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(3)), + AXP806_BLDO4_V_CTRL, AXP806_BLDO4_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO4_MASK), AXP_DESC(AXP806, CLDO1, "cldo1", "cldoin", 700, 3300, 100, - AXP806_CLDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(4)), - AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", axp803_dldo2_ranges, - 32, AXP806_CLDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, - BIT(5)), + AXP806_CLDO1_V_CTRL, AXP806_CLDO1_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO1_MASK), + AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP806_CLDO2_V_CTRL, AXP806_CLDO2_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO2_MASK), AXP_DESC(AXP806, CLDO3, "cldo3", "cldoin", 700, 3300, 100, - AXP806_CLDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(6)), - AXP_DESC_SW(AXP806, SW, "sw", "swin", AXP806_PWR_OUT_CTRL2, BIT(7)), + AXP806_CLDO3_V_CTRL, AXP806_CLDO3_V_CTRL_MASK, + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO3_MASK), + AXP_DESC_SW(AXP806, SW, "sw", "swin", + AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_SW_MASK), }; static const struct regulator_linear_range axp809_dcdc4_ranges[] = { - REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000), - REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000), + REGULATOR_LINEAR_RANGE(600000, + AXP809_DCDC4_600mV_START, + AXP809_DCDC4_600mV_END, + 20000), + REGULATOR_LINEAR_RANGE(1800000, + AXP809_DCDC4_1800mV_START, + AXP809_DCDC4_1800mV_END, + 100000), }; static const struct regulator_desc axp809_regulators[] = { AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), + AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK), AXP_DESC(AXP809, DCDC2, "dcdc2", "vin2", 600, 1540, 20, - AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)), + AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK), AXP_DESC(AXP809, DCDC3, "dcdc3", "vin3", 600, 1860, 20, - AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), - AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", axp809_dcdc4_ranges, - 57, AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), + AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", + axp809_dcdc4_ranges, AXP809_DCDC4_NUM_VOLTAGES, + AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK), AXP_DESC(AXP809, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, - AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK), /* secondary switchable output of DCDC1 */ - AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2, - BIT(7)), + AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), /* LDO regulator internally chained to DCDC5 */ AXP_DESC(AXP809, DC5LDO, "dc5ldo", NULL, 700, 1400, 100, - AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)), + AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK), AXP_DESC(AXP809, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP809, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), - AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(3)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ALDO3_MASK), + AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100, - AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)), + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP809, ELDO1, "eldo1", "eldoin", 700, 3300, 100, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP809, ELDO2, "eldo2", "eldoin", 700, 3300, 100, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP809, ELDO3, "eldo3", "eldoin", 700, 3300, 100, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* * Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), /* * Note the datasheet only guarantees reliable operation up to * 3.3V, this needs to be enforced via dts provided constraints */ AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP809, RTC_LDO, "rtc_ldo", "ips", 1800), - AXP_DESC_SW(AXP809, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP_DESC_SW(AXP809, SW, "sw", "swin", + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_SW_MASK), }; static const struct regulator_desc axp813_regulators[] = { AXP_DESC(AXP813, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, - AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)), - AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges, - 76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(1)), - AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges, - 76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(2)), - AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges, - 76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(3)), - AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges, - 68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(4)), - AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges, - 72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(5)), - AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7", axp803_dcdc6_ranges, - 72, AXP813_DCDC7_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1, - BIT(6)), + AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK), + AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK), + AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK), + AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4", + axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES, + AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK), + AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5", + axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES, + AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK), + AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK), + AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7", + axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES, + AXP813_DCDC7_V_OUT, AXP813_DCDC7_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL1, AXP813_PWR_OUT_DCDC7_MASK), AXP_DESC(AXP813, ALDO1, "aldo1", "aldoin", 700, 3300, 100, - AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)), + AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK), AXP_DESC(AXP813, ALDO2, "aldo2", "aldoin", 700, 3300, 100, - AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)), + AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK), AXP_DESC(AXP813, ALDO3, "aldo3", "aldoin", 700, 3300, 100, - AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)), + AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK), AXP_DESC(AXP813, DLDO1, "dldo1", "dldoin", 700, 3300, 100, - AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)), - AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges, - 32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, - BIT(4)), + AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK), + AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin", + axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES, + AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK), AXP_DESC(AXP813, DLDO3, "dldo3", "dldoin", 700, 3300, 100, - AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), + AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK), AXP_DESC(AXP813, DLDO4, "dldo4", "dldoin", 700, 3300, 100, - AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)), + AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK), AXP_DESC(AXP813, ELDO1, "eldo1", "eldoin", 700, 1900, 50, - AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)), + AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK), AXP_DESC(AXP813, ELDO2, "eldo2", "eldoin", 700, 1900, 50, - AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), + AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK), AXP_DESC(AXP813, ELDO3, "eldo3", "eldoin", 700, 1900, 50, - AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), + AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT, + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK), /* to do / check ... */ AXP_DESC(AXP813, FLDO1, "fldo1", "fldoin", 700, 1450, 50, - AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)), + AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK), AXP_DESC(AXP813, FLDO2, "fldo2", "fldoin", 700, 1450, 50, - AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)), + AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK, + AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK), /* * TODO: FLDO3 = {DCDC5, FLDOIN} / 2 * @@ -482,12 +853,15 @@ static const struct regulator_desc axp813_regulators[] = { */ AXP_DESC_FIXED(AXP813, RTC_LDO, "rtc-ldo", "ips", 1800), AXP_DESC_IO(AXP813, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100, - AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, + AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK, + AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_IO(AXP813, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100, - AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, + AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK, + AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), - AXP_DESC_SW(AXP813, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(7)), + AXP_DESC_SW(AXP813, SW, "sw", "swin", + AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK), }; static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) @@ -661,9 +1035,9 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP803_DCDC3: - return !!(reg & BIT(6)); + return !!(reg & AXP803_DCDC23_POLYPHASE_DUAL); case AXP803_DCDC6: - return !!(reg & BIT(5)); + return !!(reg & AXP803_DCDC56_POLYPHASE_DUAL); } break; @@ -672,12 +1046,15 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP806_DCDCB: - return (((reg & GENMASK(7, 6)) == BIT(6)) || - ((reg & GENMASK(7, 6)) == BIT(7))); + return (((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCAB_POLYPHASE_DUAL) || + ((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCABC_POLYPHASE_TRI)); case AXP806_DCDCC: - return ((reg & GENMASK(7, 6)) == BIT(7)); + return ((reg & AXP806_DCDCABC_POLYPHASE_MASK) == + AXP806_DCDCABC_POLYPHASE_TRI); case AXP806_DCDCE: - return !!(reg & BIT(5)); + return !!(reg & AXP806_DCDCDE_POLYPHASE_DUAL); } break; @@ -686,9 +1063,9 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) switch (id) { case AXP803_DCDC3: - return !!(reg & BIT(6)); + return !!(reg & AXP803_DCDC23_POLYPHASE_DUAL); case AXP803_DCDC6: - return !!(reg & BIT(5)); + return !!(reg & AXP803_DCDC56_POLYPHASE_DUAL); } break; From bb946f362388d8d36eb9059ba1c1d03290b355dc Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 9 Oct 2017 11:38:00 +0200 Subject: [PATCH 17/57] regulator: axp20x: name voltage ramping define properly The current axp20x names the ramping register 'scal' which probably means scaling. Since the register really has nothing to do with scaling, but really is the voltage ramp we rename it appropriately. Signed-off-by: Olliver Schinagl --- include/linux/mfd/axp20x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index 78dc85365c4f83..e48cb3b83d6b90 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -35,7 +35,7 @@ enum axp20x_variants { #define AXP152_ALDO_OP_MODE 0x13 #define AXP152_LDO0_CTRL 0x15 #define AXP152_DCDC2_V_OUT 0x23 -#define AXP152_DCDC2_V_SCAL 0x25 +#define AXP152_DCDC2_V_RAMP 0x25 #define AXP152_DCDC1_V_OUT 0x26 #define AXP152_DCDC3_V_OUT 0x27 #define AXP152_ALDO12_V_OUT 0x28 @@ -53,7 +53,7 @@ enum axp20x_variants { #define AXP20X_USB_OTG_STATUS 0x02 #define AXP20X_PWR_OUT_CTRL 0x12 #define AXP20X_DCDC2_V_OUT 0x23 -#define AXP20X_DCDC2_LDO3_V_SCAL 0x25 +#define AXP20X_DCDC2_LDO3_V_RAMP 0x25 #define AXP20X_DCDC3_V_OUT 0x27 #define AXP20X_LDO24_V_OUT 0x28 #define AXP20X_LDO3_V_OUT 0x29 From 27bef6e6b2a3f871c7bfac142a4db15d8f9ada45 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 6 Mar 2017 09:29:39 +0100 Subject: [PATCH 18/57] regulator: enable power later when setting up constraints When a regulator is marked as always on, it is enabled early on, when checking and setting up constraints. It makes the assumption that the bootloader properly initialized the regulator, and just in case enables the regulator anyway. Some constraints however currently get missed, such as the soft-start and ramp-delay. This causes the regulator to be enabled, without the soft-start and ramp-delay being applied, which in turn can cause high-currents or other start-up problems. By moving the always-enabled constraints later in the constraints check, we can at least ensure all constraints for the regulator are followed. Signed-off-by: Olliver Schinagl --- drivers/regulator/core.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b64b7916507f28..13466d07a7f896 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1088,17 +1088,6 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } - /* If the constraints say the regulator should be on at this point - * and we have control then make sure it is enabled. - */ - if (rdev->constraints->always_on || rdev->constraints->boot_on) { - ret = _regulator_do_enable(rdev); - if (ret < 0 && ret != -EINVAL) { - rdev_err(rdev, "failed to enable\n"); - return ret; - } - } - if ((rdev->constraints->ramp_delay || rdev->constraints->ramp_disable) && ops->set_ramp_delay) { ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); @@ -1144,6 +1133,17 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } + /* If the constraints say the regulator should be on at this point + * and we have control then make sure it is enabled. + */ + if (rdev->constraints->always_on || rdev->constraints->boot_on) { + ret = _regulator_do_enable(rdev); + if (ret < 0 && ret != -EINVAL) { + rdev_err(rdev, "failed to enable\n"); + return ret; + } + } + print_constraints(rdev); return 0; } From c4c4e61e7c1a1765dc99d15e4ed05464f1ba1cf3 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Thu, 2 Mar 2017 16:30:37 +0100 Subject: [PATCH 19/57] regulator: axp20x: AXP209: add support for set_ramp_delay The AXP209 supports ramping up voltages on several regulators such as DCDC2 and LDO3. This patch adds preliminary support for the regulator-ramp-delay property these 2 regulators. Note that the voltage ramp only works when the regulator is already enabled. E.g. when going from say 0.7 V to 3.6 V. When turning on the regulator, no voltage ramp is performed by the hardware. What this means, is that if the bootloader brings up the voltage at 0.7 V, the ramp delay property is properly applied. If however, the bootloader leaves the power off, no ramp delay is applied when the power is enabled by the regulator framework. Signed-off-by: Olliver Schinagl --- .../devicetree/bindings/mfd/axp20x.txt | 5 ++ drivers/regulator/axp20x-regulator.c | 87 +++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index 9455503b029931..741017c7e14b67 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -33,6 +33,11 @@ Required properties: - interrupt-controller: The PMIC has its own internal IRQs - #interrupt-cells: Should be set to 1 +Supported common regulator properties, see regulator.txt for more information: +- regulator-ramp-delay: sets the ramp up delay in uV/us + AXP20x/DCDC2: 1600, 800 + AXP20x/LDO3: 1600, 800 + Optional properties: - x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz AXP152/20X: range: 750-1875, Default: 1.5 MHz diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index c5deb5dc4382a2..31a393343214a3 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -51,6 +51,17 @@ #define AXP20X_PWR_OUT_DCDC2_MASK BIT_MASK(4) #define AXP20X_PWR_OUT_LDO3_MASK BIT_MASK(6) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK BIT_MASK(0) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE(x) \ + ((x) << 0) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK BIT_MASK(1) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(x) \ + ((x) << 1) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK BIT_MASK(2) +#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN BIT(2) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK BIT_MASK(3) +#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN BIT(3) + #define AXP20X_LDO4_V_OUT_1250mV_START 0x0 #define AXP20X_LDO4_V_OUT_1250mV_STEPS 0 #define AXP20X_LDO4_V_OUT_1250mV_END \ @@ -346,6 +357,81 @@ .ops = &axp20x_ops_range, \ } + +static const int axp209_dcdc2_ldo3_slew_rates[] = { + 1600, + 800, +}; + +static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) +{ + struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); + const struct regulator_desc *desc = rdev->desc; + uint8_t reg, mask, enable, cfg = 0xff; + const int *slew_rates; + int slew_rates_cnt = 0; + + if (!rdev) + return -EINVAL; + + switch (axp20x->variant) { + case AXP209_ID: + if (desc->id == AXP20X_DCDC2) { + slew_rates_cnt = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); + reg = AXP20X_DCDC2_LDO3_V_RAMP; + mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK | + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK; + enable = (ramp > 0) ? + AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN : + !AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN; + break; + } + + if (desc->id == AXP20X_LDO3) { + slew_rates = axp209_dcdc2_ldo3_slew_rates; + slew_rates = axp209_dcdc2_ldo3_slew_rates; + slew_rates_cnt = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates); + reg = AXP20X_DCDC2_LDO3_V_RAMP; + mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK | + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK; + enable = (ramp > 0) ? + AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN : + !AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN; + break; + } + + if (slew_rates_cnt > 0) + break; + + /* fall through */ + default: + /* Not supported for this regulator */ + return -ENOTSUPP; + } + + if (ramp == 0) { + cfg = enable; + } else { + int i; + + for (i = 0; i < slew_rates_cnt; i++) { + if (ramp <= slew_rates[i]) + cfg = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(i); + else + break; + } + + if (cfg == 0xff) { + dev_err(axp20x->dev, "unsupported ramp value %d", ramp); + return -EINVAL; + } + + cfg |= enable; + } + + return regmap_update_bits(axp20x->regmap, reg, mask, cfg); +} + static const struct regulator_ops axp20x_ops_fixed = { .list_voltage = regulator_list_voltage_linear, }; @@ -366,6 +452,7 @@ static const struct regulator_ops axp20x_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, + .set_ramp_delay = axp20x_set_ramp_delay, }; static const struct regulator_ops axp20x_ops_sw = { From aee0eb0674f1cf089f6c5277f4f4931289339456 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Thu, 2 Mar 2017 16:37:07 +0100 Subject: [PATCH 20/57] regulator: axp20x: add software based soft_start for LDO3 In the past, there have been words on various lists that if LDO3 is disabled in u-boot, but enabled in the DTS, the axp driver would fail to continue/hang. Several enable/disable patches have been issues to devicetree's in both the kernel and u-boot to address this issue. What really happened however, was that the AXP shuts down, without notice, without setting an interrupt. This is caused when LDO3 gets overloaded, for example with large capacitors on the LDO3 output. Normally, we would expect that the AXP would source 200 mA as per datasheet and set and trigger an interrupt when being overloaded. For some reason however, this does not happen. As a work-around, we use the soft-start constraint of the regulator node to first bring up the LDO3 to the lowest possible voltage and then enable the LDO. After that, we can set the requested voltage as per normal. Combining this setting with the regulator-ramp-delay allows LDO3 to come up slowly and staggered, potentially reducing overall inrush current. Signed-off-by: Olliver Schinagl --- .../devicetree/bindings/mfd/axp20x.txt | 3 + drivers/regulator/axp20x-regulator.c | 56 ++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt index 741017c7e14b67..582dcc2a3b289c 100644 --- a/Documentation/devicetree/bindings/mfd/axp20x.txt +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -37,6 +37,9 @@ Supported common regulator properties, see regulator.txt for more information: - regulator-ramp-delay: sets the ramp up delay in uV/us AXP20x/DCDC2: 1600, 800 AXP20x/LDO3: 1600, 800 +- regulator-soft-start: enable the output at the lowest possible voltage and + only then set the desired voltage + AXP20x/LDO3 Optional properties: - x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 31a393343214a3..7841287ddb3cd2 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #define AXP20X_GPIO0_FUNC_MASK GENMASK(3, 0) @@ -432,6 +433,59 @@ static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp) return regmap_update_bits(axp20x->regmap, reg, mask, cfg); } +static int axp20x_regulator_enable_regmap(struct regulator_dev *rdev) +{ + struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); + const struct regulator_desc *desc = rdev->desc; + + if (!rdev) + return -EINVAL; + + switch (axp20x->variant) { + case AXP209_ID: + if ((desc->id == AXP20X_LDO3) && + rdev->constraints && rdev->constraints->soft_start) { + int v_out; + int ret; + + /* + * On some boards, the LDO3 can be overloaded when + * turning on, causing the entire PMIC to shutdown + * without warning. Turning it on at the minimal voltage + * and then setting the voltage to the requested value + * works reliably. + */ + if (regulator_is_enabled_regmap(rdev)) + break; + + v_out = regulator_get_voltage_sel_regmap(rdev); + if (v_out < 0) + return v_out; + + if (v_out == 0) + break; + + ret = regulator_set_voltage_sel_regmap(rdev, 0x00); + /* + * A small pause is needed between + * setting the voltage and enabling the LDO to give the + * internal state machine time to process the request. + */ + udelay(1000); + ret |= regulator_enable_regmap(rdev); + ret |= regulator_set_voltage_sel_regmap(rdev, v_out); + + return ret; + } + break; + default: + /* No quirks */ + break; + } + + return regulator_enable_regmap(rdev); +}; + static const struct regulator_ops axp20x_ops_fixed = { .list_voltage = regulator_list_voltage_linear, }; @@ -449,7 +503,7 @@ static const struct regulator_ops axp20x_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, - .enable = regulator_enable_regmap, + .enable = axp20x_regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .set_ramp_delay = axp20x_set_ramp_delay, From eedcd47f2a459cce1f80f1792291ae541abd8a80 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Fri, 3 Mar 2017 10:03:45 +0100 Subject: [PATCH 21/57] arm: dts: sunxi: enable soft-start and ramp delay for the OLinuXino Lime2 The OLinuXino Lime2 has a big capacitor on its LDO3 output. It is actually too large, causing the PMIC to shutdown when toggling the LDO3. By enabling soft-start and ramp delay we increase the time for the capacitor to charge lowering the current drain on the power regulator. Signed-off-by: Olliver Schinagl --- arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts index ba250189d07f66..4fa93b98d8678e 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts @@ -246,6 +246,8 @@ regulator-min-microvolt = <2800000>; regulator-max-microvolt = <2800000>; regulator-name = "vddio-csi0"; + regulator-soft-start; + regulator-ramp-delay = <1600>; }; ®_ldo4 { From 2d11ed23a22e7ce0aebd77abc545ca7cbe25eb05 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 11 Dec 2017 14:57:13 +0100 Subject: [PATCH 22/57] dts: sunxi: Lime2: add full voltage range to LDO4 With commit b43776d65a33b46092 ("ARM: dts: sunxi: Use axp209.dtsi for Olinuxino Lime2") we force them an arbitrary 2.8 volts. Granted, for LDO3 this may be less arbitrary, but for LDO4 this is just wrong. In the defense of LDO3, LDO3 is the regulator that feeds port bank E, which has no other purpose then a CSI/TS interface, however the case may still be, that the connected IO may be just as well be 3.3 volts. The big misnomer is however, that the schematic names GPIO-2 pin4 LDO3_2.8V, rather then VDD-CSI0 or similar. This is much worse for LDO4 however, which is not referenced on any pin, is now set to 2.8 volts, but port bank G can also support various other peripherals such as UARTS etc. By having 2.8 volts however for LDO4, we thus now have peripherals that no longer function properly all of the time. Ideally, we want to set a supply voltage for each port bank, but the monolithic nature of the sunxi pinctroller currently prevents this and as such, the board should at least configure the LDO4 with the proper ranges. Until we can set the consumer at the port bank level, a child device-tree has to do something like: ®_ldo4 { regulator-min-microvolt = <3300000>; regulator-max-microvolt = <3300000>; }; While doing this the same way results in the same solution currently, we force the hack into the final devicetree rather then having it wrong at the board level. Signed-off-by: Olliver Schinagl --- arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts index 4fa93b98d8678e..9cf8d99ab95f30 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts @@ -251,9 +251,10 @@ }; ®_ldo4 { - regulator-min-microvolt = <2800000>; - regulator-max-microvolt = <2800000>; - regulator-name = "vddio-csi1"; + regulator-always-on; + regulator-min-microvolt = <1250000>; + regulator-max-microvolt = <3300000>; + regulator-name = "vdd-io-pg"; }; ®_usb0_vbus { From d2ab365bdd4147085b53910443a658b08b4433e8 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 18 Dec 2017 15:29:33 +0100 Subject: [PATCH 23/57] dts: sunxi: OLinuXino Lime2: set proper lradc vref The lradc's analog reference voltage is set to 3.0 volt in the hardware. This is more or less set in stone for atleast lradc0. Set the property in the dts to ensure the lradc is referenced properly. Signed-off-by: Olliver Schinagl --- arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts index 9cf8d99ab95f30..18230822192afe 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts @@ -154,6 +154,10 @@ }; }; +&lradc { + vref-supply = <®_vcc3v0>; +}; + &mmc0 { pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins_a>; From aeea6b1a71c9b67aaf105dd57c8dd323fddef445 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 8 Mar 2017 10:34:49 +0100 Subject: [PATCH 24/57] pwm: core: do not block apply->state on period The old pwm_config call, did some parameter sanitation such as preventing to change the pwm config if the period is 0. With the new atomic framework, we now not only apply configuration parameters, but also the polarity setting. The polarity setting however can be applied both with an enabled or disabled PWM, in other words, setting the polarity is perfectly acceptable when the period is 0. This patch removes the check if the period is valid and instead, sets the state disabled if the period is 0. It now thus becomes possible to set the polarity, before changing any other PWM parameters. The reason this is needed is that if someone calls pwm_set_polarity with the period set to 0, pwm_set_polarity would fail. Having period of 0 is however perfectly acceptable here. Signed-off-by: Olliver Schinagl --- drivers/pwm/core.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 1581f6ab1b1f42..ecca745dce8e24 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -468,13 +468,15 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) { int err; - if (!pwm || !state || !state->period || - state->duty_cycle > state->period) + if (!pwm || !state || state->duty_cycle > state->period) return -EINVAL; if (!memcmp(state, &pwm->state, sizeof(*state))) return 0; + if (state->period == 0) + state->enabled = false; + if (pwm->chip->ops->apply) { err = pwm->chip->ops->apply(pwm->chip, pwm, state); if (err) @@ -507,8 +509,9 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) pwm->state.polarity = state->polarity; } - if (state->period != pwm->state.period || - state->duty_cycle != pwm->state.duty_cycle) { + if (state->period && + (state->period != pwm->state.period || + state->duty_cycle != pwm->state.duty_cycle)) { err = pwm->chip->ops->config(pwm->chip, pwm, state->duty_cycle, state->period); From ed71150ca245b7c81cd67ac54c5bc86debc2c78e Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 11 Dec 2017 16:33:28 +0100 Subject: [PATCH 25/57] input: of_touchscreen: fix whitespace Fix alignment whitespace, no code changes where performed. Signed-off-by: Olliver Schinagl --- .../devicetree/bindings/input/touchscreen/touchscreen.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt index 537643e86f6186..5b51cde4218849 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt @@ -14,7 +14,7 @@ Optional properties for Touchscreens: - touchscreen-fuzz-pressure : pressure noise value of the absolute input device (arbitrary range dependent on the controller) - - touchscreen-average-samples : Number of data samples which are averaged + - touchscreen-average-samples : Number of data samples which are averaged for each read (valid values dependent on the controller) - touchscreen-inverted-x : X axis is inverted (boolean) From de61fc58f7d88a8899fd55fa0aaf423fc5c6a02c Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 11 Dec 2017 16:20:21 +0100 Subject: [PATCH 26/57] input: of_touchscreen: shorten variable names To clean up some whitespace issues and make room for future additions some variable names where shortened. This patch does not invoke any code changes. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/of_touchscreen.c | 56 ++++++++++------------ 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index 8d7f9c8f2771c7..7c278fe033c77e 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -68,46 +68,42 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, { struct device *dev = input->dev.parent; unsigned int axis; - unsigned int maximum, fuzz; - bool data_present; + unsigned int max, fuzz; + bool ret; input_alloc_absinfo(input); if (!input->absinfo) return; axis = multitouch ? ABS_MT_POSITION_X : ABS_X; - data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-x", - input_abs_get_max(input, - axis) + 1, - &maximum) | - touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x", - input_abs_get_fuzz(input, axis), - &fuzz); - if (data_present) - touchscreen_set_params(input, axis, maximum - 1, fuzz); + ret = touchscreen_get_prop_u32(dev, "touchscreen-size-x", + input_abs_get_max(input, axis) + 1, + &max) | + touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x", + input_abs_get_fuzz(input, axis), + &fuzz); + if (ret) + touchscreen_set_params(input, axis, max - 1, fuzz); axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y; - data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-y", - input_abs_get_max(input, - axis) + 1, - &maximum) | - touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y", - input_abs_get_fuzz(input, axis), - &fuzz); - if (data_present) - touchscreen_set_params(input, axis, maximum - 1, fuzz); + ret = touchscreen_get_prop_u32(dev, "touchscreen-size-y", + input_abs_get_max(input, axis) + 1, + &max) | + touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y", + input_abs_get_fuzz(input, axis), + &fuzz); + if (ret) + touchscreen_set_params(input, axis, max - 1, fuzz); axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE; - data_present = touchscreen_get_prop_u32(dev, - "touchscreen-max-pressure", - input_abs_get_max(input, axis), - &maximum) | - touchscreen_get_prop_u32(dev, - "touchscreen-fuzz-pressure", - input_abs_get_fuzz(input, axis), - &fuzz); - if (data_present) - touchscreen_set_params(input, axis, maximum, fuzz); + ret = touchscreen_get_prop_u32(dev, "touchscreen-max-pressure", + input_abs_get_max(input, axis), + &max) | + touchscreen_get_prop_u32(dev, "touchscreen-fuzz-pressure", + input_abs_get_fuzz(input, axis), + &fuzz); + if (ret) + touchscreen_set_params(input, axis, max, fuzz); if (!prop) return; From 3d28f056505a887fefab2f3cd2aebc5e8463541c Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 11 Dec 2017 16:26:41 +0100 Subject: [PATCH 27/57] input: of_touchscreen: rename touchscreen-size-[xy] The parameter touchscreen-size-x sits oddly next to touchscreen-max-pressure for example. Further more when considering that the actual parameter modified in absinfo is the 'maximum'. Let us thus rename the touchscreen-max-size-[xy] property in the devicetree. The new name takes precedence over the earlier touchscreen-size-[xy] in case both are found. The former is now deprecated. Signed-off-by: Olliver Schinagl --- .../bindings/input/touchscreen/touchscreen.txt | 10 ++++++---- drivers/input/touchscreen/of_touchscreen.c | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt index 5b51cde4218849..76c406d1b8f687 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt @@ -1,10 +1,10 @@ General Touchscreen Properties: Optional properties for Touchscreens: - - touchscreen-size-x : horizontal resolution of touchscreen - (in pixels) - - touchscreen-size-y : vertical resolution of touchscreen - (in pixels) + - touchscreen-max-size-x : maximal reported horizontal resolution of + touchscreen (in pixels) + - touchscreen-max-size-y : maximal reported vertical resolution of touchscreen + touchscreen (in pixels) - touchscreen-max-pressure : maximum reported pressure (arbitrary range dependent on the controller) - touchscreen-fuzz-x : horizontal noise value of the absolute input @@ -25,6 +25,8 @@ Optional properties for Touchscreens: - touchscreen-y-mm : vertical length in mm of the touchscreen Deprecated properties for Touchscreens: + - touchscreen-size-x : deprecated name for touchscreen-size-x + - touchscreen-size-y : deprecated name for touchscreen-size-y - x-size : deprecated name for touchscreen-size-x - y-size : deprecated name for touchscreen-size-y - moving-threshold : deprecated name for a combination of diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index 7c278fe033c77e..9872c31cea1bd3 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -79,6 +79,9 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, ret = touchscreen_get_prop_u32(dev, "touchscreen-size-x", input_abs_get_max(input, axis) + 1, &max) | + touchscreen_get_prop_u32(dev, "touchscreen-max-size-x", + input_abs_get_max(input, axis) + 1, + &max) | touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x", input_abs_get_fuzz(input, axis), &fuzz); @@ -89,6 +92,9 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, ret = touchscreen_get_prop_u32(dev, "touchscreen-size-y", input_abs_get_max(input, axis) + 1, &max) | + touchscreen_get_prop_u32(dev, "touchscreen-max-size-y", + input_abs_get_max(input, axis) + 1, + &max) | touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y", input_abs_get_fuzz(input, axis), &fuzz); From fe302044dcbcfbaf0014280c5d0185c1fdeafa50 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 13 Dec 2017 13:45:08 +0100 Subject: [PATCH 28/57] input: of_touchscreen: add support for minimum sizes In certain configurations, touchscreens are bigger then the underlying display. This can happen for various reasons, but an interesting design choice is to improve the edge detection of touch events. To make sure we don't have strange offsets we want to pass this information along to the input framework via the of_touchscreen layer. To this, we need to also parse the 'minimum' property. Signed-off-by: Olliver Schinagl --- .../input/touchscreen/touchscreen.txt | 4 ++++ drivers/input/touchscreen/of_touchscreen.c | 21 ++++++++++++------- include/linux/input/touchscreen.h | 2 ++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt index 76c406d1b8f687..34ef910e907a14 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt @@ -1,6 +1,10 @@ General Touchscreen Properties: Optional properties for Touchscreens: + - touchscreen-min-size-x : minimal reported horizontal resolution of + touchscreen (in pixels) + - touchscreen-min-size-y : minimal reported vertical resolution of + touchscreen (in pixels) - touchscreen-max-size-x : maximal reported horizontal resolution of touchscreen (in pixels) - touchscreen-max-size-y : maximal reported vertical resolution of touchscreen diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index 9872c31cea1bd3..c12f3c863feb3a 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -34,7 +34,7 @@ static bool touchscreen_get_prop_u32(struct device *dev, static void touchscreen_set_params(struct input_dev *dev, unsigned long axis, - int max, int fuzz) + int min, int max, int fuzz) { struct input_absinfo *absinfo; @@ -46,6 +46,7 @@ static void touchscreen_set_params(struct input_dev *dev, } absinfo = &dev->absinfo[axis]; + absinfo->minimum = min; absinfo->maximum = max; absinfo->fuzz = fuzz; } @@ -68,7 +69,7 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, { struct device *dev = input->dev.parent; unsigned int axis; - unsigned int max, fuzz; + unsigned int min, max, fuzz; bool ret; input_alloc_absinfo(input); @@ -76,7 +77,10 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, return; axis = multitouch ? ABS_MT_POSITION_X : ABS_X; - ret = touchscreen_get_prop_u32(dev, "touchscreen-size-x", + ret = touchscreen_get_prop_u32(dev, "touchscreen-min-size-x", + input_abs_get_min(input, axis), + &min) | + touchscreen_get_prop_u32(dev, "touchscreen-size-x", input_abs_get_max(input, axis) + 1, &max) | touchscreen_get_prop_u32(dev, "touchscreen-max-size-x", @@ -86,10 +90,13 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, input_abs_get_fuzz(input, axis), &fuzz); if (ret) - touchscreen_set_params(input, axis, max - 1, fuzz); + touchscreen_set_params(input, axis, min, max - 1, fuzz); axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y; - ret = touchscreen_get_prop_u32(dev, "touchscreen-size-y", + ret = touchscreen_get_prop_u32(dev, "touchscreen-min-size-y", + input_abs_get_min(input, axis), + &min) | + touchscreen_get_prop_u32(dev, "touchscreen-size-y", input_abs_get_max(input, axis) + 1, &max) | touchscreen_get_prop_u32(dev, "touchscreen-max-size-y", @@ -99,7 +106,7 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, input_abs_get_fuzz(input, axis), &fuzz); if (ret) - touchscreen_set_params(input, axis, max - 1, fuzz); + touchscreen_set_params(input, axis, min, max - 1, fuzz); axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE; ret = touchscreen_get_prop_u32(dev, "touchscreen-max-pressure", @@ -109,7 +116,7 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch, input_abs_get_fuzz(input, axis), &fuzz); if (ret) - touchscreen_set_params(input, axis, max, fuzz); + touchscreen_set_params(input, axis, 0, max, fuzz); if (!prop) return; diff --git a/include/linux/input/touchscreen.h b/include/linux/input/touchscreen.h index 09d22ccb9e415e..3c94403183b14b 100644 --- a/include/linux/input/touchscreen.h +++ b/include/linux/input/touchscreen.h @@ -13,7 +13,9 @@ struct input_dev; struct input_mt_pos; struct touchscreen_properties { + unsigned int min_x; unsigned int max_x; + unsigned int min_y; unsigned int max_y; bool invert_x; bool invert_y; From 688d6e2750e87f82ddcc07e9c7ee247d92d93b84 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 13 Dec 2017 14:52:51 +0100 Subject: [PATCH 29/57] input: edt-ft5x06: add support for the ft5426 controller The current ft5x06 driver happily supports the ft5426 controller chip. Lets add a compatible for it. Signed-off-by: Olliver Schinagl --- .../devicetree/bindings/input/touchscreen/edt-ft5x06.txt | 1 + drivers/input/touchscreen/edt-ft5x06.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt index 025cf8c9324ac3..a86b3dd13cb27d 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -18,6 +18,7 @@ Required properties: - compatible: "edt,edt-ft5206" or: "edt,edt-ft5306" or: "edt,edt-ft5406" + or: "edt,edt-ft5426" or: "edt,edt-ft5506" or: "focaltech,ft6236" diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 5bf63f76dddac6..502c6e8626cf8c 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1069,6 +1069,7 @@ static const struct edt_i2c_chip_data edt_ft6236_data = { static const struct i2c_device_id edt_ft5x06_ts_id[] = { { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, + { .name = "edt-ft5426", .driver_data = (long)&edt_ft5506_data }, { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, /* Note no edt- prefix for compatibility with the ft6236.c driver */ { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, @@ -1081,6 +1082,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = { { .compatible = "edt,edt-ft5206", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data }, { .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data }, + { .compatible = "edt,edt-ft5426", .data = &edt_ft5506_data }, { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, /* Note focaltech vendor prefix for compatibility with ft6236.c */ { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, From 0d4e3e70aa0783f47e5c7af1a9e1a40cb21a46c3 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 13 Dec 2017 14:57:52 +0100 Subject: [PATCH 30/57] input: edt-ft5x06: cleanup headers Alphabetize and add missing headers to the edt-ft5x06 driver. No code changes where done in this commit. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 502c6e8626cf8c..5bc35dc3679691 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -25,20 +25,28 @@ * http://www.glyn.com/Products/Displays */ -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include +#include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #define WORK_REGISTER_THRESHOLD 0x00 #define WORK_REGISTER_REPORT_RATE 0x08 From c957a5d3f7ce6bc94100f93357d95b87bf3434af Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 13 Dec 2017 15:42:27 +0100 Subject: [PATCH 31/57] input: edt-ft5x06: only enable the irq when needed The input framework has callbacks for when an input device gets opened/closed to do certain actions. Use these to enable/disable interrupts when they are not in use. This can then be improved in the future by doing power savings when the device is not in use. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 5bc35dc3679691..f1f72a0fe03412 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -764,6 +764,22 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ +static int edt_ft5x06_open(struct input_dev *dev) +{ + struct edt_ft5x06_ts_data *tsdata = input_get_drvdata(dev); + + enable_irq(tsdata->client->irq); + + return 0; +} + +static void edt_ft5x06_close(struct input_dev *dev) +{ + struct edt_ft5x06_ts_data *tsdata = input_get_drvdata(dev); + + disable_irq(tsdata->client->irq); +} + static int edt_ft5x06_ts_identify(struct i2c_client *client, struct edt_ft5x06_ts_data *tsdata, char *fw_version) @@ -976,6 +992,8 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input->name = tsdata->name; input->id.bustype = BUS_I2C; input->dev.parent = &client->dev; + input->open = edt_ft5x06_open; + input->close = edt_ft5x06_close; input_set_abs_params(input, ABS_MT_POSITION_X, 0, tsdata->num_x * 64 - 1, 0, 0); @@ -991,6 +1009,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; } + input_set_drvdata(input, tsdata); i2c_set_clientdata(client, tsdata); irq_flags = irq_get_trigger_type(client->irq); @@ -1005,6 +1024,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); return error; } + disable_irq(client->irq); error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group); if (error) From fb86a27ff2b01f46287e0a22bd40377ef19056e3 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Thu, 14 Dec 2017 09:55:44 +0100 Subject: [PATCH 32/57] input: edt-ft5x06: fix some whitespace/ident issues This patch just cleans up some ident/whitespacing issues. No code changes where performed. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index f1f72a0fe03412..ec35467a6eb676 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -643,7 +643,8 @@ DEFINE_SIMPLE_ATTRIBUTE(debugfs_mode_fops, edt_ft5x06_debugfs_mode_get, edt_ft5x06_debugfs_mode_set, "%llu\n"); static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file, - char __user *buf, size_t count, loff_t *off) + char __user *buf, + size_t count, loff_t *off) { struct edt_ft5x06_ts_data *tsdata = file->private_data; struct i2c_client *client = tsdata->client; @@ -781,8 +782,8 @@ static void edt_ft5x06_close(struct input_dev *dev) } static int edt_ft5x06_ts_identify(struct i2c_client *client, - struct edt_ft5x06_ts_data *tsdata, - char *fw_version) + struct edt_ft5x06_ts_data *tsdata, + char *fw_version) { u8 rdbuf[EDT_NAME_LEN]; char *p; @@ -908,7 +909,7 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) } static int edt_ft5x06_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { const struct edt_i2c_chip_data *chip_data; struct edt_ft5x06_ts_data *tsdata; @@ -1003,7 +1004,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, touchscreen_parse_properties(input, true, &tsdata->prop); error = input_mt_init_slots(input, tsdata->max_support_points, - INPUT_MT_DIRECT); + INPUT_MT_DIRECT); if (error) { dev_err(&client->dev, "Unable to init MT slots.\n"); return error; From d862555b93c9868d9d35dda0ce6b625122100882 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Thu, 14 Dec 2017 10:04:23 +0100 Subject: [PATCH 33/57] input: edt-ft5x06: minor consistency cleanup/line length reduction The struct device is often referenced in the probe function via &client->dev. We can shorten this by introducing a compile time removed pointer to be able to just type 'dev'. This reduces some of our longer function line lengths improving readability just a little bit. While touching said lines, (mostly dev_*() print strings) make the strings more consistent (with itself and the kernel output) by dropping caps at the start and periods at the end having at least a concistent style within the same function. No logical changes where performed. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 59 +++++++++++++------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index ec35467a6eb676..e65e6f81a0fe3a 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -912,45 +912,45 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { const struct edt_i2c_chip_data *chip_data; + struct device *dev = &client->dev; struct edt_ft5x06_ts_data *tsdata; struct input_dev *input; unsigned long irq_flags; int error; char fw_version[EDT_NAME_LEN]; - dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); + dev_dbg(dev, "probing for EDT FT5x06 I2C\n"); - tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); + tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL); if (!tsdata) { - dev_err(&client->dev, "failed to allocate driver data.\n"); + dev_err(dev, "failed to allocate driver data\n"); return -ENOMEM; } - chip_data = of_device_get_match_data(&client->dev); + chip_data = of_device_get_match_data(dev); if (!chip_data) chip_data = (const struct edt_i2c_chip_data *)id->driver_data; if (!chip_data || !chip_data->max_support_points) { - dev_err(&client->dev, "invalid or missing chip data\n"); + dev_err(dev, "invalid or missing chip data\n"); return -EINVAL; } tsdata->max_support_points = chip_data->max_support_points; - tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev, + tsdata->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(tsdata->reset_gpio)) { error = PTR_ERR(tsdata->reset_gpio); - dev_err(&client->dev, - "Failed to request GPIO reset pin, error %d\n", error); + dev_err(dev, "failed to request GPIO reset pin, error %d\n", + error); return error; } - tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev, - "wake", GPIOD_OUT_LOW); + tsdata->wake_gpio = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW); if (IS_ERR(tsdata->wake_gpio)) { error = PTR_ERR(tsdata->wake_gpio); - dev_err(&client->dev, - "Failed to request GPIO wake pin, error %d\n", error); + dev_err(dev, "failed to request GPIO wake pin, error %d\n", + error); return error; } @@ -965,9 +965,9 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, msleep(300); } - input = devm_input_allocate_device(&client->dev); + input = devm_input_allocate_device(dev); if (!input) { - dev_err(&client->dev, "failed to allocate input device.\n"); + dev_err(dev, "failed to allocate input device\n"); return -ENOMEM; } @@ -978,21 +978,20 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, error = edt_ft5x06_ts_identify(client, tsdata, fw_version); if (error) { - dev_err(&client->dev, "touchscreen probe failed\n"); + dev_err(dev, "touchscreen probe failed\n"); return error; } edt_ft5x06_ts_set_regs(tsdata); - edt_ft5x06_ts_get_defaults(&client->dev, tsdata); + edt_ft5x06_ts_get_defaults(dev, tsdata); edt_ft5x06_ts_get_parameters(tsdata); - dev_dbg(&client->dev, - "Model \"%s\", Rev. \"%s\", %dx%d sensors\n", + dev_dbg(dev, "model \"%s\", Rev. \"%s\", %dx%d sensors\n", tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); input->name = tsdata->name; input->id.bustype = BUS_I2C; - input->dev.parent = &client->dev; + input->dev.parent = dev; input->open = edt_ft5x06_open; input->close = edt_ft5x06_close; @@ -1006,7 +1005,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, error = input_mt_init_slots(input, tsdata->max_support_points, INPUT_MT_DIRECT); if (error) { - dev_err(&client->dev, "Unable to init MT slots.\n"); + dev_err(dev, "unable to init MT slots\n"); return error; } @@ -1018,16 +1017,16 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, irq_flags = IRQF_TRIGGER_FALLING; irq_flags |= IRQF_ONESHOT; - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, edt_ft5x06_ts_isr, irq_flags, - client->name, tsdata); + error = devm_request_threaded_irq(dev, client->irq, NULL, + edt_ft5x06_ts_isr, irq_flags, + client->name, tsdata); if (error) { - dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); + dev_err(dev, "unable to request touchscreen IRQ\n"); return error; } disable_irq(client->irq); - error = sysfs_create_group(&client->dev.kobj, &edt_ft5x06_attr_group); + error = sysfs_create_group(&dev->kobj, &edt_ft5x06_attr_group); if (error) return error; @@ -1035,11 +1034,11 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, if (error) goto err_remove_attrs; - edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); - device_init_wakeup(&client->dev, 1); + edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(dev)); + device_init_wakeup(dev, 1); - dev_dbg(&client->dev, - "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", + dev_dbg(dev, + "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d\n", client->irq, tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1, tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1); @@ -1047,7 +1046,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return 0; err_remove_attrs: - sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); + sysfs_remove_group(&dev->kobj, &edt_ft5x06_attr_group); return error; } From 5f010504ea23a38fe3d2914ea3c62796f2bd1b7a Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Thu, 14 Dec 2017 11:02:00 +0100 Subject: [PATCH 34/57] input: edt-ft5x06: shorten defines Drop the long _REGISTER_ string in the M09_ macro's. It makes our register defines really long and doesn't add anything. By shortening the defines we can introduce more macro's that are more descriptive and potentially longer. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index e65e6f81a0fe3a..8a537f3e16979e 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -55,11 +55,11 @@ #define WORK_REGISTER_NUM_X 0x33 #define WORK_REGISTER_NUM_Y 0x34 -#define M09_REGISTER_THRESHOLD 0x80 -#define M09_REGISTER_GAIN 0x92 -#define M09_REGISTER_OFFSET 0x93 -#define M09_REGISTER_NUM_X 0x94 -#define M09_REGISTER_NUM_Y 0x95 +#define M09_THRESHOLD 0x80 +#define M09_GAIN 0x92 +#define M09_OFFSET 0x93 +#define M09_NUM_X 0x94 +#define M09_NUM_Y 0x95 #define NO_REGISTER 0xff @@ -475,11 +475,11 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, } static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, - M09_REGISTER_GAIN, 0, 31); + M09_GAIN, 0, 31); static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, - M09_REGISTER_OFFSET, 0, 31); + M09_OFFSET, 0, 31); static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, - M09_REGISTER_THRESHOLD, 0, 80); + M09_THRESHOLD, 0, 80); static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, NO_REGISTER, 3, 14); @@ -898,12 +898,12 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) break; case M09: - reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; + reg_addr->reg_threshold = M09_THRESHOLD; reg_addr->reg_report_rate = NO_REGISTER; - reg_addr->reg_gain = M09_REGISTER_GAIN; - reg_addr->reg_offset = M09_REGISTER_OFFSET; - reg_addr->reg_num_x = M09_REGISTER_NUM_X; - reg_addr->reg_num_y = M09_REGISTER_NUM_Y; + reg_addr->reg_gain = M09_GAIN; + reg_addr->reg_offset = M09_OFFSET; + reg_addr->reg_num_x = M09_NUM_X; + reg_addr->reg_num_y = M09_NUM_Y; break; } } From 992e1c7abf7b4f81804e76cb689268cbd20eb160 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Fri, 15 Dec 2017 14:12:33 +0100 Subject: [PATCH 35/57] input: edt-ft5x06: use less magic and more defines We can simply add some more defines to remove some magic values and thus create more readable code. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 88 +++++++++++++++++++------- 1 file changed, 64 insertions(+), 24 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 8a537f3e16979e..64464d410a2d7c 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -25,6 +25,7 @@ * http://www.glyn.com/Products/Displays */ +#include #include #include #include @@ -55,28 +56,57 @@ #define WORK_REGISTER_NUM_X 0x33 #define WORK_REGISTER_NUM_Y 0x34 +#define M06_TOUCH_REPORT 0x05 +#define M06_TOUCH_REPORT_LEN 4 +#define M06_TOUCH_REPORT_CRC_LEN 1 +#define M06_TOUCH_REPORT_HEADER_H 0x00 +#define M06_TOUCH_REPORT_HEADER_L 0x01 +#define M06_TOUCH_REPORT_DATALEN 0x02 +#define M06_TOUCH_REPORT_MAGIC 0xaa + +#define M09_TD_STATUS 0x02 +#define M09_TOUCH_REPORT 0x03 +#define M09_TOUCH_REPORT_LEN 6 + +#define EDT_TOUCH_XH_EVENT 0x00 +#define EDT_TOUCH_XH_MASK GENMASK(3, 0) +#define EDT_TOUCH_EVENT_DOWN 0x00 +#define EDT_TOUCH_EVENT_UP BIT(6) +#define EDT_TOUCH_EVENT_ON BIT(7) +#define EDT_TOUCH_EVENT_RESERVED \ + (EDT_TOUCH_EVENT_UP | EDT_TOUCH_EVENT_ON) +#define EDT_TOUCH_EVENT_FLAG_MASK GENMASK(7, 6) +#define EDT_TOUCH_XL 0x01 +#define EDT_TOUCH_ID_YH 0x02 +#define EDT_TOUCH_YH_MASK GENMASK(3, 0) +#define EDT_TOUCH_ID_MASK GENMASK(7, 4) +#define EDT_TOUCH_ID_SHIFT 4 +#define EDT_TOUCH_YL 0x03 + #define M09_THRESHOLD 0x80 #define M09_GAIN 0x92 #define M09_OFFSET 0x93 #define M09_NUM_X 0x94 #define M09_NUM_Y 0x95 +#define M06_TOUCH_REPORT_REQ 0xf9 + #define NO_REGISTER 0xff #define WORK_REGISTER_OPMODE 0x3c #define FACTORY_REGISTER_OPMODE 0x01 -#define TOUCH_EVENT_DOWN 0x00 -#define TOUCH_EVENT_UP 0x01 -#define TOUCH_EVENT_ON 0x02 -#define TOUCH_EVENT_RESERVED 0x03 - #define EDT_NAME_LEN 23 #define EDT_SWITCH_MODE_RETRIES 10 #define EDT_SWITCH_MODE_DELAY 5 /* msec */ #define EDT_RAW_DATA_RETRIES 100 #define EDT_RAW_DATA_DELAY 1000 /* usec */ +#define EDT_TOUCH_REPORT_MAX_SIZE ((10 * M09_TOUCH_REPORT_LEN) + \ + M09_TOUCH_REPORT_LEN + \ + M06_TOUCH_REPORT + \ + M06_TOUCH_REPORT_CRC_LEN) + enum edt_ver { M06, M09, @@ -181,23 +211,23 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) struct edt_ft5x06_ts_data *tsdata = dev_id; struct device *dev = &tsdata->client->dev; u8 cmd; - u8 rdbuf[63]; + u8 rdbuf[EDT_TOUCH_REPORT_MAX_SIZE]; int i, type, x, y, id; int offset, tplen, datalen, crclen; int error; switch (tsdata->version) { case M06: - cmd = 0xf9; /* tell the controller to send touch data */ - offset = 5; /* where the actual touch data starts */ - tplen = 4; /* data comes in so called frames */ - crclen = 1; /* length of the crc data */ + cmd = M06_TOUCH_REPORT_REQ; /* tell the controller to send touch data */ + offset = M06_TOUCH_REPORT; /* where the actual touch data starts */ + tplen = M06_TOUCH_REPORT_LEN; /* data comes in so called frames */ + crclen = M06_TOUCH_REPORT_CRC_LEN; /* length of the crc data */ break; case M09: cmd = 0x0; - offset = 3; - tplen = 6; + offset = M09_TOUCH_REPORT; + tplen = M09_TOUCH_REPORT_LEN; crclen = 0; break; @@ -219,12 +249,15 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) /* M09 does not send header or CRC */ if (tsdata->version == M06) { - if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || - rdbuf[2] != datalen) { + if (rdbuf[M06_TOUCH_REPORT_HEADER_H] != M06_TOUCH_REPORT_MAGIC || + rdbuf[M06_TOUCH_REPORT_HEADER_L] != M06_TOUCH_REPORT_MAGIC || + rdbuf[M06_TOUCH_REPORT_DATALEN] != datalen) { dev_err_ratelimited(dev, - "Unexpected header: %02x%02x%02x!\n", - rdbuf[0], rdbuf[1], rdbuf[2]); - goto out; + "Unexpected header: %02x%02x%02x!\n", + rdbuf[M06_TOUCH_REPORT_HEADER_H], + rdbuf[M06_TOUCH_REPORT_HEADER_L], + rdbuf[M06_TOUCH_REPORT_DATALEN]); + goto: out; } if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) @@ -235,19 +268,26 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) u8 *buf = &rdbuf[i * tplen + offset]; bool down; - type = buf[0] >> 6; + type = buf[EDT_TOUCH_XH_EVENT] & EDT_TOUCH_EVENT_FLAG_MASK; /* ignore Reserved events */ - if (type == TOUCH_EVENT_RESERVED) + if (type == EDT_TOUCH_EVENT_RESERVED) continue; /* M06 sometimes sends bogus coordinates in TOUCH_DOWN */ - if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN) + if (tsdata->version == M06 && type == EDT_TOUCH_EVENT_DOWN) continue; - x = ((buf[0] << 8) | buf[1]) & 0x0fff; - y = ((buf[2] << 8) | buf[3]) & 0x0fff; - id = (buf[2] >> 4) & 0x0f; - down = type != TOUCH_EVENT_UP; + x = buf[EDT_TOUCH_XH_EVENT] & EDT_TOUCH_XH_MASK; + x <<= BITS_PER_BYTE; + x |= buf[EDT_TOUCH_XL]; + + y = buf[EDT_TOUCH_ID_YH] & EDT_TOUCH_YH_MASK; + y <<= BITS_PER_BYTE; + y |= buf[EDT_TOUCH_YL]; + + id = (buf[EDT_TOUCH_ID_YH] & EDT_TOUCH_ID_MASK); + id >>= EDT_TOUCH_ID_SHIFT; + down = type != EDT_TOUCH_EVENT_UP; input_mt_slot(tsdata->input, id); input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); From 046c81013090b1e625e5617232b08137e94ddb12 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Fri, 15 Dec 2017 14:13:34 +0100 Subject: [PATCH 36/57] input: edt-ft5x06: silence deferral error Currently when we get a deferral request on an optional GPIO, we print out a failure message making the user think something bad has happened. Instead, as commonly done, silence the -EPROBE_DEFER message as it is an acceptable 'error'. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 64464d410a2d7c..f90b52ffdc63e9 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -981,16 +981,18 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, "reset", GPIOD_OUT_HIGH); if (IS_ERR(tsdata->reset_gpio)) { error = PTR_ERR(tsdata->reset_gpio); - dev_err(dev, "failed to request GPIO reset pin, error %d\n", - error); + if (error != -EPROBE_DEFER) + dev_err(dev, "failed to request GPIO reset pin: %d\n", + error); return error; } tsdata->wake_gpio = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW); if (IS_ERR(tsdata->wake_gpio)) { error = PTR_ERR(tsdata->wake_gpio); - dev_err(dev, "failed to request GPIO wake pin, error %d\n", - error); + if (error != -EPROBE_DEFER) + dev_err(dev, "failed to request GPIO wake pin: %d\n", + error); return error; } From 7207bb7dee56aae9c29d7ee9772c6ffcf7558986 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 18 Dec 2017 10:36:45 +0100 Subject: [PATCH 37/57] input: edt-ft5x06: sanity check on input It may happen, that tsdata or tsdata->input are NULL. One observed example was that when requesting the IRQ, but not having all structures setup properly. Add a simple sanity check for NULL, just in case. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index f90b52ffdc63e9..a87276ff057a2e 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -216,6 +216,9 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) int offset, tplen, datalen, crclen; int error; + if ((!tsdata) || (!tsdata->input)) + return; + switch (tsdata->version) { case M06: cmd = M06_TOUCH_REPORT_REQ; /* tell the controller to send touch data */ From b68c90b51c6b6ddf87b037c742cbfd85bf68e156 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 18 Dec 2017 10:39:18 +0100 Subject: [PATCH 38/57] input: edt-ft5x06: fix multi-touch handling Multi-touch handling is currently incorrectly handled by the edt-ft5x06 driver. According to Documentation/input/multi-touch-protocol.rst, we are supposed to: ABS_MT_SLOT 0 ABS_MT_TRACKING_ID 45 ABS_MT_POSITION_X x[0] ABS_MT_POSITION_Y y[0] ABS_MT_SLOT 1 ABS_MT_TRACKING_ID 46 ABS_MT_POSITION_X x[1] ABS_MT_POSITION_Y y[1] SYN_REPORT and: Here is the sequence after lifting the contact in slot 0:: ABS_MT_TRACKING_ID -1 SYN_REPORT What happens in our driver however, is: ABS_MT_SLOT 45 ABS_MT_POSITION_X x[0] ABS_MT_POSITION_Y y[0] ABS_MT_SLOT 46 ABS_MT_POSITION_X x[1] ABS_MT_POSITION_Y y[1] SYN_REPORT and for lifting nothing is done. This is obviously wrong, as we are allocating a slot based on the tracking id, not based on the input (finger) and worse, never reset tracking id to -1 to 'free' them. Refactor the ISR to follow the spec by allocating a slot based on finger and set/clear the tracking ID once the finger is no longer down. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index a87276ff057a2e..59c26ca9624170 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -271,7 +271,16 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) u8 *buf = &rdbuf[i * tplen + offset]; bool down; + input_mt_slot(tsdata->input, i); + type = buf[EDT_TOUCH_XH_EVENT] & EDT_TOUCH_EVENT_FLAG_MASK; + + down = type != EDT_TOUCH_EVENT_UP; + if (!down) { + input_report_abs(tsdata->input, ABS_MT_TRACKING_ID, -1); + continue; + } + /* ignore Reserved events */ if (type == EDT_TOUCH_EVENT_RESERVED) continue; @@ -280,6 +289,12 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (tsdata->version == M06 && type == EDT_TOUCH_EVENT_DOWN) continue; + input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); + + id = (buf[EDT_TOUCH_ID_YH] & EDT_TOUCH_ID_MASK); + id >>= EDT_TOUCH_ID_SHIFT; + input_report_abs(tsdata->input, ABS_MT_TRACKING_ID, id); + x = buf[EDT_TOUCH_XH_EVENT] & EDT_TOUCH_XH_MASK; x <<= BITS_PER_BYTE; x |= buf[EDT_TOUCH_XL]; @@ -288,16 +303,6 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) y <<= BITS_PER_BYTE; y |= buf[EDT_TOUCH_YL]; - id = (buf[EDT_TOUCH_ID_YH] & EDT_TOUCH_ID_MASK); - id >>= EDT_TOUCH_ID_SHIFT; - down = type != EDT_TOUCH_EVENT_UP; - - input_mt_slot(tsdata->input, id); - input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); - - if (!down) - continue; - touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y, true); } From cb474efef2e62fb1d4a974a5a80fd709552f88be Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 18 Dec 2017 11:07:40 +0100 Subject: [PATCH 39/57] input: edt-ft5x06: take into account the number of fingers It may happen that we miss a touch !down event, for example the interrupt fires, but we are to late to read the correct data. When this happens, we may wrongfully set/clear ABS_MT_TRACKING_ID to -1. The M09 firmware actually tells us how many fingers are being touched. By using that, we also thus know how many events to focus on and to clear unused tracking id's. This also has as a boon that we save some processing time by not evaluating the registers for unused touches. Note: this should not be used to reduce the number of bytes read, e.g. read the number of touches register, followed by a read for the actual registers, as that would result in a non-atomic read, and our data could be out of sync. E.g. we are told to 4 inputs have been pressed, but by the time we actually read these registers, 5 inputs are actually being touched. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 59c26ca9624170..891958144ea1c4 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -213,7 +213,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) u8 cmd; u8 rdbuf[EDT_TOUCH_REPORT_MAX_SIZE]; int i, type, x, y, id; - int offset, tplen, datalen, crclen; + int offset, touch_cnt, tplen, datalen, crclen; int error; if ((!tsdata) || (!tsdata->input)) @@ -221,6 +221,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) switch (tsdata->version) { case M06: + touch_cnt = tsdata->max_support_points; cmd = M06_TOUCH_REPORT_REQ; /* tell the controller to send touch data */ offset = M06_TOUCH_REPORT; /* where the actual touch data starts */ tplen = M06_TOUCH_REPORT_LEN; /* data comes in so called frames */ @@ -228,6 +229,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) break; case M09: + touch_cnt = 0; cmd = 0x0; offset = M09_TOUCH_REPORT; tplen = M09_TOUCH_REPORT_LEN; @@ -265,6 +267,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) goto out; + } else { + touch_cnt = rdbuf[M09_TD_STATUS]; } for (i = 0; i < tsdata->max_support_points; i++) { @@ -276,7 +280,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) type = buf[EDT_TOUCH_XH_EVENT] & EDT_TOUCH_EVENT_FLAG_MASK; down = type != EDT_TOUCH_EVENT_UP; - if (!down) { + if (!down || i > touch_cnt) { input_report_abs(tsdata->input, ABS_MT_TRACKING_ID, -1); continue; } From 15ecee1cbc2d4a356924bdd8e99ac8259da2d889 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 18 Dec 2017 11:16:12 +0100 Subject: [PATCH 40/57] input: edt-ft5x06: add polldev as an option The edt-ft5x06 is not required to be used with an interrupt. We can just as easily poll the edt-ft5x06 using the polldev framework. This is needed as some electrical designs may omit the IRQ line. Signed-off-by: Olliver Schinagl --- .../bindings/input/touchscreen/edt-ft5x06.txt | 5 +- drivers/input/touchscreen/edt-ft5x06.c | 148 +++++++++++++----- 2 files changed, 116 insertions(+), 37 deletions(-) diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt index a86b3dd13cb27d..ab01900ea73ad2 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -23,12 +23,12 @@ Required properties: or: "focaltech,ft6236" - reg: I2C slave address of the chip (0x38) + +Optional properties: - interrupt-parent: a phandle pointing to the interrupt controller serving the interrupt for this chip - interrupts: interrupt specification for the touchdetect interrupt - -Optional properties: - reset-gpios: GPIO specification for the RESET input - wake-gpios: GPIO specification for the WAKE input @@ -45,6 +45,7 @@ Optional properties: - offset: allows setting the edge compensation in the range from 0 to 31. + - poll-interval: Poll interval time in milliseconds - touchscreen-size-x : See touchscreen.txt - touchscreen-size-y : See touchscreen.txt - touchscreen-fuzz-x : See touchscreen.txt diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 891958144ea1c4..67de81d545874a 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -34,12 +34,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -47,6 +49,7 @@ #include #include #include +#include #include #define WORK_REGISTER_THRESHOLD 0x00 @@ -89,6 +92,10 @@ #define M09_NUM_X 0x94 #define M09_NUM_Y 0x95 +#define M09_ID_G_MODE 0xa4 +#define M09_ID_G_MODE_POLL 0x00 +#define M09_ID_G_MODE_IRQ 0x01 + #define M06_TOUCH_REPORT_REQ 0xf9 #define NO_REGISTER 0xff @@ -112,6 +119,11 @@ enum edt_ver { M09, }; +enum readout_mode { + EDT_READOUT_MODE_POLL, + EDT_READOUT_MODE_IRQ, +}; + struct edt_reg_addr { int reg_threshold; int reg_report_rate; @@ -124,6 +136,9 @@ struct edt_reg_addr { struct edt_ft5x06_ts_data { struct i2c_client *client; struct input_dev *input; +#if IS_ENABLED(CONFIG_INPUT_POLLDEV) + struct input_polled_dev *polldev; +#endif struct touchscreen_properties prop; u16 num_x; u16 num_y; @@ -206,9 +221,8 @@ static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, return true; } -static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) +static void edt_ft5x06_report(struct edt_ft5x06_ts_data *tsdata) { - struct edt_ft5x06_ts_data *tsdata = dev_id; struct device *dev = &tsdata->client->dev; u8 cmd; u8 rdbuf[EDT_TOUCH_REPORT_MAX_SIZE]; @@ -237,7 +251,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) break; default: - goto out; + return; } memset(rdbuf, 0, sizeof(rdbuf)); @@ -249,7 +263,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (error) { dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", error); - goto out; + return; } /* M09 does not send header or CRC */ @@ -262,11 +276,11 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) rdbuf[M06_TOUCH_REPORT_HEADER_H], rdbuf[M06_TOUCH_REPORT_HEADER_L], rdbuf[M06_TOUCH_REPORT_DATALEN]); - goto: out; + return; } if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) - goto out; + return; } else { touch_cnt = rdbuf[M09_TD_STATUS]; } @@ -313,11 +327,24 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) input_mt_report_pointer_emulation(tsdata->input, true); input_sync(tsdata->input); +} + +static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) +{ + struct edt_ft5x06_ts_data *tsdata = dev_id; + + edt_ft5x06_report(tsdata); -out: return IRQ_HANDLED; } +static void edt_ft5x06_poll(struct input_polled_dev *polldev) +{ + struct edt_ft5x06_ts_data *tsdata = polldev->private; + + edt_ft5x06_report(tsdata); +} + static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, u8 addr, u8 value) { @@ -934,6 +961,24 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y); } +static void edt_ft5x06_ts_set_readout_mode(struct edt_ft5x06_ts_data *tsdata, + enum readout_mode mode) +{ + uint8_t readout_mode; + + switch (tsdata->version) { + case M06: + break; + case M09: + readout_mode = (mode == EDT_READOUT_MODE_POLL) ? + M09_ID_G_MODE_POLL : + M09_ID_G_MODE_IRQ; + edt_ft5x06_register_write(tsdata, M09_ID_G_MODE, readout_mode); + break; + } +} + + static void edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) { @@ -967,7 +1012,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, struct device *dev = &client->dev; struct edt_ft5x06_ts_data *tsdata; struct input_dev *input; - unsigned long irq_flags; int error; char fw_version[EDT_NAME_LEN]; @@ -1019,15 +1063,8 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, msleep(300); } - input = devm_input_allocate_device(dev); - if (!input) { - dev_err(dev, "failed to allocate input device\n"); - return -ENOMEM; - } - mutex_init(&tsdata->mutex); tsdata->client = client; - tsdata->input = input; tsdata->factory_mode = false; error = edt_ft5x06_ts_identify(client, tsdata, fw_version); @@ -1043,11 +1080,66 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, dev_dbg(dev, "model \"%s\", Rev. \"%s\", %dx%d sensors\n", tsdata->name, fw_version, tsdata->num_x, tsdata->num_y); + i2c_set_clientdata(client, tsdata); + + if (client->irq) { + unsigned long irq_flags; + + irq_flags = irq_get_trigger_type(client->irq); + if (irq_flags == IRQF_TRIGGER_NONE) + irq_flags = IRQF_TRIGGER_FALLING; + irq_flags |= IRQF_ONESHOT; + + error = devm_request_threaded_irq(dev, client->irq, NULL, + edt_ft5x06_ts_isr, irq_flags, + client->name, tsdata); + if (error) { + dev_err(dev, "unable to request touchscreen IRQ\n"); + return error; + } + + disable_irq(client->irq); + + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "failed to allocate input device\n"); + return -ENOMEM; + } + input->open = edt_ft5x06_open; + input->close = edt_ft5x06_close; + + edt_ft5x06_ts_set_readout_mode(tsdata, EDT_READOUT_MODE_IRQ); + } else { +#if !IS_ENABLED(CONFIG_INPUT_POLLDEV) + dev_err(dev, "no IRQ setup and built without INPUT_POLLDEV\n"); + return -ENODEV; +#else + uint32_t poll_interval; + + dev_warn(dev, "no IRQ setup, using polled input\n"); + + tsdata->polldev = devm_input_allocate_polled_device(dev); + if (!tsdata->polldev) { + dev_err(dev, "failed to allocate polldev\n"); + return -ENOMEM; + } + + if (!device_property_read_u32(dev, "poll-interval", &poll_interval)) + tsdata->polldev->poll_interval = poll_interval; + + tsdata->polldev->private = tsdata; + tsdata->polldev->poll = edt_ft5x06_poll; + input = tsdata->polldev->input; + + edt_ft5x06_ts_set_readout_mode(tsdata, EDT_READOUT_MODE_POLL); +#endif + } + input->name = tsdata->name; input->id.bustype = BUS_I2C; input->dev.parent = dev; - input->open = edt_ft5x06_open; - input->close = edt_ft5x06_close; + input_set_drvdata(input, tsdata); + tsdata->input = input; input_set_abs_params(input, ABS_MT_POSITION_X, 0, tsdata->num_x * 64 - 1, 0, 0); @@ -1063,28 +1155,14 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; } - input_set_drvdata(input, tsdata); - i2c_set_clientdata(client, tsdata); - - irq_flags = irq_get_trigger_type(client->irq); - if (irq_flags == IRQF_TRIGGER_NONE) - irq_flags = IRQF_TRIGGER_FALLING; - irq_flags |= IRQF_ONESHOT; - - error = devm_request_threaded_irq(dev, client->irq, NULL, - edt_ft5x06_ts_isr, irq_flags, - client->name, tsdata); - if (error) { - dev_err(dev, "unable to request touchscreen IRQ\n"); - return error; - } - disable_irq(client->irq); - error = sysfs_create_group(&dev->kobj, &edt_ft5x06_attr_group); if (error) return error; - error = input_register_device(input); + if (client->irq) + error = input_register_device(input); + else + error = input_register_polled_device(tsdata->polldev); if (error) goto err_remove_attrs; From 5777ab19879e6acd43b9cbcc08109790b2889c37 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 19 Dec 2017 16:24:11 +0100 Subject: [PATCH 41/57] input: edt-ft5x06: group r, w and rw functions Move the read and write functions higher and next to the read-write function. This makes adding features easier without introducing prototypes and keeps the relevant functions together. This patch introduces no code changes. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 132 ++++++++++++------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 67de81d545874a..99df956f9d8108 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -202,6 +202,72 @@ static int edt_ft5x06_ts_readwrite(struct i2c_client *client, return 0; } +static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, + u8 addr, u8 value) +{ + u8 wrbuf[4]; + + switch (tsdata->version) { + case M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[2] = value; + wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; + return edt_ft5x06_ts_readwrite(tsdata->client, 4, + wrbuf, 0, NULL); + case M09: + wrbuf[0] = addr; + wrbuf[1] = value; + + return edt_ft5x06_ts_readwrite(tsdata->client, 2, + wrbuf, 0, NULL); + + default: + return -EINVAL; + } +} + +static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, + u8 addr) +{ + u8 wrbuf[2], rdbuf[2]; + int error; + + switch (tsdata->version) { + case M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; + + error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, + rdbuf); + if (error) + return error; + + if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { + dev_err(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], + rdbuf[1]); + return -EIO; + } + break; + + case M09: + wrbuf[0] = addr; + error = edt_ft5x06_ts_readwrite(tsdata->client, 1, + wrbuf, 1, rdbuf); + if (error) + return error; + break; + + default: + return -EINVAL; + } + + return rdbuf[0]; +} + static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata, u8 *buf, int buflen) { @@ -345,72 +411,6 @@ static void edt_ft5x06_poll(struct input_polled_dev *polldev) edt_ft5x06_report(tsdata); } -static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, - u8 addr, u8 value) -{ - u8 wrbuf[4]; - - switch (tsdata->version) { - case M06: - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[2] = value; - wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; - return edt_ft5x06_ts_readwrite(tsdata->client, 4, - wrbuf, 0, NULL); - case M09: - wrbuf[0] = addr; - wrbuf[1] = value; - - return edt_ft5x06_ts_readwrite(tsdata->client, 2, - wrbuf, 0, NULL); - - default: - return -EINVAL; - } -} - -static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, - u8 addr) -{ - u8 wrbuf[2], rdbuf[2]; - int error; - - switch (tsdata->version) { - case M06: - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; - - error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, - rdbuf); - if (error) - return error; - - if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { - dev_err(&tsdata->client->dev, - "crc error: 0x%02x expected, got 0x%02x\n", - wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], - rdbuf[1]); - return -EIO; - } - break; - - case M09: - wrbuf[0] = addr; - error = edt_ft5x06_ts_readwrite(tsdata->client, 1, - wrbuf, 1, rdbuf); - if (error) - return error; - break; - - default: - return -EINVAL; - } - - return rdbuf[0]; -} - struct edt_ft5x06_attribute { struct device_attribute dattr; size_t field_offset; From 4f8b339c3b3192a80c1b7fda1f481e1a94a1103f Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 19 Dec 2017 16:28:50 +0100 Subject: [PATCH 42/57] input: edt-ft5x06: Force touchscreen to remain active With the M09, the touchscreen enters sleep mode after a few seconds. During this time the controller responds to all i2c commands normally, but no input data is generated. This patch forces active mode during each poll interval, to prevent sleep mode (monitor mode) to be activated during normal operation. This can be further improved by setting/clearing the bit when going into suspend/resume mode. Some extra logic would be needed there however to only go into suspend mode if a reset/wake line is available. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/edt-ft5x06.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 99df956f9d8108..9a9d3bc4f55e8e 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -96,6 +96,11 @@ #define M09_ID_G_MODE_POLL 0x00 #define M09_ID_G_MODE_IRQ 0x01 +#define M09_ID_G_PMODE 0xa5 +#define M09_ID_G_PMODE_ACTIVE 0x00 +#define M09_ID_G_PMODE_MONITOR 0x01 +#define M09_ID_G_PMODE_HIBERNATE 0x03 + #define M06_TOUCH_REPORT_REQ 0xf9 #define NO_REGISTER 0xff @@ -408,6 +413,15 @@ static void edt_ft5x06_poll(struct input_polled_dev *polldev) { struct edt_ft5x06_ts_data *tsdata = polldev->private; + switch (tsdata->version) { + case M06: + break; + case M09: + edt_ft5x06_register_write(tsdata, M09_ID_G_PMODE, + M09_ID_G_PMODE_ACTIVE); + break; + } + edt_ft5x06_report(tsdata); } From 93bf96d8b6d5df3b5c60c20d398fa8e5ea85bad1 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Thu, 19 Oct 2017 14:06:39 +0200 Subject: [PATCH 43/57] input: ar1021: fix typo in define Not sure what TOCUH is, but i'm fairly certain 'TOUCH' was meant. Signed-off-by: Olliver Schinagl --- drivers/input/touchscreen/ar1021_i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index f9dcbd63e59826..8938f80e1e963a 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -14,7 +14,7 @@ #include #include -#define AR1021_TOCUH_PKG_SIZE 5 +#define AR1021_TOUCH_PKG_SIZE 5 #define AR1021_MAX_X 4095 #define AR1021_MAX_Y 4095 @@ -26,7 +26,7 @@ struct ar1021_i2c { struct i2c_client *client; struct input_dev *input; - u8 data[AR1021_TOCUH_PKG_SIZE]; + u8 data[AR1021_TOUCH_PKG_SIZE]; }; static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id) From 35ba7133c10ed429f475877c6378307dc69b1ce8 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 18 Dec 2017 11:58:32 +0100 Subject: [PATCH 44/57] gpio: pca953x: add support for the NXP pca9570 The PCA9570 is the most basic of the pca957x series. It is a 4 bit i2c gpio expander without an interrupt line. Signed-off-by: Olliver Schinagl --- Documentation/devicetree/bindings/gpio/gpio-pca953x.txt | 1 + drivers/gpio/gpio-pca953x.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt index 7f57271df2bc96..78511d54cafb88 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt @@ -13,6 +13,7 @@ Required properties: nxp,pca9555 nxp,pca9556 nxp,pca9557 + nxp,pca9570 nxp,pca9574 nxp,pca9575 nxp,pca9698 diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 1b9dbf691ae7a8..1e82f956a7d40e 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -66,6 +66,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pca9555", 16 | PCA953X_TYPE | PCA_INT, }, { "pca9556", 8 | PCA953X_TYPE, }, { "pca9557", 8 | PCA953X_TYPE, }, + { "pca9570", 4 | PCA957X_TYPE, }, { "pca9574", 8 | PCA957X_TYPE | PCA_INT, }, { "pca9575", 16 | PCA957X_TYPE | PCA_INT, }, { "pca9698", 40 | PCA953X_TYPE, }, @@ -931,6 +932,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pca9555", .data = OF_953X(16, PCA_INT), }, { .compatible = "nxp,pca9556", .data = OF_953X( 8, 0), }, { .compatible = "nxp,pca9557", .data = OF_953X( 8, 0), }, + { .compatible = "nxp,pca9570", .data = OF_957X( 4, 0), }, { .compatible = "nxp,pca9574", .data = OF_957X( 8, PCA_INT), }, { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), }, { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), }, From 8872877d72fae7cdc1d0d94e317c21b1531604a1 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 9 Oct 2017 15:53:03 +0200 Subject: [PATCH 45/57] ASoC: sun4i-is: also check for NULL on reset pin request Commit 2ad6f30de7087 ("ASoC: sun4i-i2s: Add quirks to handle a31 compatible") added support for reset control but support for sun4i was overlooked. For these older SoC's it is quite common not have this reset control. The reset control is first requested, and later checked if it IS_ERR. If however there is no reset control configured, we can get NULL and thus the checks pass with a NULL pointer passed the functions afterwards. Lets use IS_ERR_OR_NULL to ensure the reset control is only (de)asserted when we actually have a valid reset control. Signed-off-by: Olliver Schinagl --- sound/soc/sunxi/sun4i-i2s.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 04f92583a96965..351b7b6400c7ca 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -1033,13 +1033,13 @@ static int sun4i_i2s_probe(struct platform_device *pdev) if (i2s->variant->has_reset) { i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(i2s->rst)) { + if (IS_ERR_OR_NULL(i2s->rst)) { dev_err(&pdev->dev, "Failed to get reset control\n"); return PTR_ERR(i2s->rst); } } - if (!IS_ERR(i2s->rst)) { + if (!IS_ERR_OR_NULL(i2s->rst)) { ret = reset_control_deassert(i2s->rst); if (ret) { dev_err(&pdev->dev, @@ -1089,7 +1089,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev) sun4i_i2s_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - if (!IS_ERR(i2s->rst)) + if (!IS_ERR_OR_NULL(i2s->rst)) reset_control_assert(i2s->rst); return ret; From b126923734dfdad723f5b9284be246f8cfbe07cc Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 29 Mar 2017 20:29:58 +0200 Subject: [PATCH 46/57] serial: 8250_dw: minor code cleanup Some very minor code cleanups, such as including the bitops header for DW_UART_MCR_SIRE, use the BIT() macro as suggested by checkpatch and removed some white spacing. Signed-off-by: Olliver Schinagl --- drivers/tty/serial/8250/8250_dw.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 7e638997bfc2ca..d50bc5a5848f1b 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -13,6 +13,7 @@ * LCR is written whilst busy. If it is, then a busy detect interrupt is * raised, the LCR needs to be rewritten and the uart status register read. */ +#include #include #include #include @@ -39,16 +40,16 @@ /* Component Parameter Register bits */ #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) -#define DW_UART_CPR_AFCE_MODE (1 << 4) -#define DW_UART_CPR_THRE_MODE (1 << 5) -#define DW_UART_CPR_SIR_MODE (1 << 6) -#define DW_UART_CPR_SIR_LP_MODE (1 << 7) -#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8) -#define DW_UART_CPR_FIFO_ACCESS (1 << 9) -#define DW_UART_CPR_FIFO_STAT (1 << 10) -#define DW_UART_CPR_SHADOW (1 << 11) -#define DW_UART_CPR_ENCODED_PARMS (1 << 12) -#define DW_UART_CPR_DMA_EXTRA (1 << 13) +#define DW_UART_CPR_AFCE_MODE BIT(4) +#define DW_UART_CPR_THRE_MODE BIT(5) +#define DW_UART_CPR_SIR_MODE BIT(6) +#define DW_UART_CPR_SIR_LP_MODE BIT(7) +#define DW_UART_CPR_ADDITIONAL_FEATURES BIT(8) +#define DW_UART_CPR_FIFO_ACCESS BIT(9) +#define DW_UART_CPR_FIFO_STAT BIT(10) +#define DW_UART_CPR_SHADOW BIT(11) +#define DW_UART_CPR_ENCODED_PARMS BIT(12) +#define DW_UART_CPR_DMA_EXTRA BIT(13) #define DW_UART_CPR_FIFO_MODE (0xff << 16) /* Helper for fifo size calculation */ #define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) @@ -193,12 +194,11 @@ static void dw8250_serial_out32be(struct uart_port *p, int offset, int value) static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset) { - unsigned int value = ioread32be(p->membase + (offset << p->regshift)); + unsigned int value = ioread32be(p->membase + (offset << p->regshift)); - return dw8250_modify_msr(p, offset, value); + return dw8250_modify_msr(p, offset, value); } - static int dw8250_handle_irq(struct uart_port *p) { struct uart_8250_port *up = up_to_u8250p(p); @@ -222,7 +222,7 @@ static int dw8250_handle_irq(struct uart_port *p) status = p->serial_in(p, UART_LSR); if (!(status & (UART_LSR_DR | UART_LSR_BI))) - (void) p->serial_in(p, UART_RX); + (void)p->serial_in(p, UART_RX); spin_unlock_irqrestore(&p->lock, flags); } From 40d3e78bda98d773f5814c0a1e381aedaf5f7606 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 29 Mar 2017 20:21:42 +0200 Subject: [PATCH 47/57] serial: do not treat the IIR register as a bitfield It seems that at some point, someone made the assumption that the UART Interrupt ID Register was a bitfield and started to check if certain bits where set. Actually however the register contains interrupt ID's where only the MSB seems to be used singular and the rest share at least one bit. Thus doing bitfield operations is wrong. This patch cleans up the serial_reg include file by ordering it and replacing the UART_IIR_ID 'mask' with a proper mask for the register. The OMAP uart appears to have used the two commonly 'reserved' bits 4 and 5 and thus get an UART_IIR_EXT_MASK for these two bits. This patch then goes over all UART_IIR_* users and changes the code from bitfield checking, to ID checking instead. Signed-off-by: Olliver Schinagl --- drivers/tty/serial/8250/8250_core.c | 8 +++++--- drivers/tty/serial/8250/8250_dw.c | 5 +++-- drivers/tty/serial/8250/8250_fsl.c | 3 ++- drivers/tty/serial/8250/8250_omap.c | 4 ++-- drivers/tty/serial/8250/8250_port.c | 11 ++++++----- drivers/tty/serial/omap-serial.c | 12 ++++++------ drivers/tty/serial/pxa.c | 2 +- drivers/tty/serial/serial-tegra.c | 3 ++- drivers/tty/serial/sunsu.c | 2 +- drivers/tty/serial/vr41xx_siu.c | 2 +- include/uapi/linux/serial_reg.h | 8 ++++---- 11 files changed, 33 insertions(+), 27 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index d29b512a7d9fac..9d83b0dbe5a15b 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -288,6 +288,7 @@ static void serial8250_backup_timeout(unsigned long data) } iir = serial_in(up, UART_IIR); + iir &= UART_IIR_MASK; /* * This should be a safe test for anyone who doesn't trust the @@ -297,14 +298,15 @@ static void serial8250_backup_timeout(unsigned long data) */ lsr = serial_in(up, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && + if ((iir == UART_IIR_NO_INT) && + (up->ier & UART_IER_THRI) && (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && (lsr & UART_LSR_THRE)) { - iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); + iir &= ~(UART_IIR_MASK); iir |= UART_IIR_THRI; } - if (!(iir & UART_IIR_NO_INT)) + if (iir != UART_IIR_NO_INT) serial8250_tx_chars(up); if (up->port.irq) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index d50bc5a5848f1b..c7b4ca0949280a 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -217,7 +217,8 @@ static int dw8250_handle_irq(struct uart_port *p) * This problem has only been observed so far when not in DMA mode * so we limit the workaround only to non-DMA mode. */ - if (!up->dma && ((iir & 0x3f) == UART_IIR_RX_TIMEOUT)) { + iir &= UART_IIR_MASK; + if (!up->dma && (iir == UART_IIR_RX_TIMEOUT)) { spin_lock_irqsave(&p->lock, flags); status = p->serial_in(p, UART_LSR); @@ -230,7 +231,7 @@ static int dw8250_handle_irq(struct uart_port *p) if (serial8250_handle_irq(p, iir)) return 1; - if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { + if (iir == UART_IIR_BUSY) { /* Clear the USR */ (void)p->serial_in(p, d->usr_reg); diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index 910bfee5a88b7f..b866343bb485ca 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -33,7 +33,8 @@ int fsl8250_handle_irq(struct uart_port *port) spin_lock_irqsave(&up->port.lock, flags); iir = port->serial_in(port, UART_IIR); - if (iir & UART_IIR_NO_INT) { + iir &= UART_IIR_MASK; + if (iir == UART_IIR_NO_INT) { spin_unlock_irqrestore(&up->port.lock, flags); return 0; } diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 833771bca0a593..f9ab3aad4579f8 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1019,7 +1019,7 @@ static int omap_8250_tx_dma(struct uart_8250_port *p) static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) { - switch (iir & 0x3f) { + switch (iir & (UART_IIR_MASK | UART_IIR_EXT_MASK)) { case UART_IIR_RLSI: case UART_IIR_RX_TIMEOUT: case UART_IIR_RDI: @@ -1044,7 +1044,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) serial8250_rpm_get(up); iir = serial_port_in(port, UART_IIR); - if (iir & UART_IIR_NO_INT) { + if ((iir & UART_IIR_MASK) == UART_IIR_NO_INT) { serial8250_rpm_put(up); return 0; } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index f0cc04f62b6768..8af9cbb057162b 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1836,7 +1836,7 @@ EXPORT_SYMBOL_GPL(serial8250_modem_status); static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) { - switch (iir & 0x3f) { + switch (iir & UART_IIR_MASK) { case UART_IIR_RX_TIMEOUT: serial8250_rx_dma_flush(up); /* fall-through */ @@ -1855,7 +1855,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) unsigned long flags; struct uart_8250_port *up = up_to_u8250p(port); - if (iir & UART_IIR_NO_INT) + if ((iir & UART_IIR_MASK) == UART_IIR_NO_INT) return 0; spin_lock_irqsave(&port->lock, flags); @@ -1922,7 +1922,7 @@ static int serial8250_tx_threshold_handle_irq(struct uart_port *port) unsigned int iir = serial_port_in(port, UART_IIR); /* TX Threshold IRQ triggered so load up FIFO */ - if ((iir & UART_IIR_ID) == UART_IIR_THRI) { + if ((iir & UART_IIR_MASK) == UART_IIR_THRI) { struct uart_8250_port *up = up_to_u8250p(port); spin_lock_irqsave(&port->lock, flags); @@ -2290,7 +2290,8 @@ int serial8250_do_startup(struct uart_port *port) * don't trust the iir, setup a timer to kick the UART * on a regular basis. */ - if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) || + if ((((iir1 & UART_IIR_MASK) != UART_IIR_NO_INT) && + ((iir & UART_IIR_MASK) == UART_IIR_NO_INT)) || up->port.flags & UPF_BUG_THRE) { up->bugs |= UART_BUG_THRE; } @@ -2341,7 +2342,7 @@ int serial8250_do_startup(struct uart_port *port) iir = serial_port_in(port, UART_IIR); serial_port_out(port, UART_IER, 0); - if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + if (lsr & UART_LSR_TEMT && ((iir & UART_IIR_MASK) == UART_IIR_NO_INT)) { if (!(up->bugs & UART_BUG_TXEN)) { up->bugs |= UART_BUG_TXEN; pr_debug("ttyS%d - enabling bad tx status workarounds\n", diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 7754053deedac9..9054c8855c5029 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -569,7 +569,6 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) { struct uart_omap_port *up = dev_id; unsigned int iir, lsr; - unsigned int type; irqreturn_t ret = IRQ_NONE; int max_count = 256; @@ -578,16 +577,15 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) do { iir = serial_in(up, UART_IIR); - if (iir & UART_IIR_NO_INT) + iir &= (UART_IIR_MASK | UART_IIR_EXT_MASK); + if (iir == UART_IIR_NO_INT) break; ret = IRQ_HANDLED; lsr = serial_in(up, UART_LSR); /* extract IRQ type from IIR register */ - type = iir & 0x3e; - - switch (type) { + switch (iir) { case UART_IIR_MSI: check_modem_status(up); break; @@ -607,10 +605,12 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) break; case UART_IIR_XOFF: /* FALLTHROUGH */ + case UART_IIR_BUSY: + /* FALLTHROUGH */ default: break; } - } while (!(iir & UART_IIR_NO_INT) && max_count--); + } while ((iir != UART_IIR_NO_INT) && max_count--); spin_unlock(&up->port.lock); diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 905631df1f8bab..37cb17a11b347c 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -253,7 +253,7 @@ static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id) unsigned int iir, lsr; iir = serial_in(up, UART_IIR); - if (iir & UART_IIR_NO_INT) + if ((iir & UART_IIR_MASK) == UART_IIR_NO_INT) return IRQ_NONE; spin_lock(&up->port.lock); lsr = serial_in(up, UART_LSR); diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index cf9b736f26f88e..c5a07215794583 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -691,7 +691,8 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) spin_lock_irqsave(&u->lock, flags); while (1) { iir = tegra_uart_read(tup, UART_IIR); - if (iir & UART_IIR_NO_INT) { + iir &= UART_IIR_MASK; + if (iir == UART_IIR_NO_INT) { if (is_rx_int) { tegra_uart_handle_rx_dma(tup); if (tup->rx_in_progress) { diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 95d34d7565c9aa..05161bafc551ba 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -475,7 +475,7 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) spin_lock_irqsave(&up->port.lock, flags); - } while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)); + } while ((serial_in(up, UART_IIR) & UART_IIR_MASK) != UART_IIR_NO_INT); spin_unlock_irqrestore(&up->port.lock, flags); diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c index 439057e8107a10..fc3b9bd920e98b 100644 --- a/drivers/tty/serial/vr41xx_siu.c +++ b/drivers/tty/serial/vr41xx_siu.c @@ -429,7 +429,7 @@ static irqreturn_t siu_interrupt(int irq, void *dev_id) port = (struct uart_port *)dev_id; iir = siu_read(port, UART_IIR); - if (iir & UART_IIR_NO_INT) + if ((iir & UART_IIR_MASK) == UART_IIR_NO_INT) return IRQ_NONE; lsr = siu_read(port, UART_LSR); diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 5db76880b4adfc..489522389a10c2 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -31,18 +31,18 @@ #define UART_IERX_SLEEP 0x10 /* Enable sleep mode */ #define UART_IIR 2 /* In: Interrupt ID Register */ -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x0e /* Mask for the interrupt ID */ #define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ #define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ #define UART_IIR_RDI 0x04 /* Receiver data interrupt */ #define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ - #define UART_IIR_BUSY 0x07 /* DesignWare APB Busy Detect */ +#define UART_IIR_RX_TIMEOUT 0x0c /* DesignWare RX Timeout interrupt */ +#define UART_IIR_MASK 0x0f /* DesignWare IIR mask */ -#define UART_IIR_RX_TIMEOUT 0x0c /* OMAP RX Timeout interrupt */ #define UART_IIR_XOFF 0x10 /* OMAP XOFF/Special Character */ #define UART_IIR_CTS_RTS_DSR 0x20 /* OMAP CTS/RTS/DSR Change */ +#define UART_IIR_EXT_MASK 0x30 /* OMAP extended IIR mask */ #define UART_FCR 2 /* Out: FIFO Control Register */ #define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ From 6b66bba34470eddeb111946488d2ef85d635c5c6 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 29 Mar 2017 20:27:03 +0200 Subject: [PATCH 48/57] serial: tegra: map the iir register to default defines The tegra serial IP seems to be following the common layout and the interrupt ID's match up nicely. Replace the magic values to match the common serial_reg defines, with the addition of the Tegra unique End of Data interrupt. Signed-off-by: Olliver Schinagl --- drivers/tty/serial/serial-tegra.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index c5a07215794583..b2db2e0ec533f6 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -83,6 +83,8 @@ #define TEGRA_TX_PIO 1 #define TEGRA_TX_DMA 2 +#define TEGRA_UART_IIR_EOD 0x8 + /** * tegra_uart_chip_data: SOC specific data. * @@ -707,20 +709,20 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) return IRQ_HANDLED; } - switch ((iir >> 1) & 0x7) { - case 0: /* Modem signal change interrupt */ + switch (iir) { + case UART_IIR_MSI: /* Modem signal change interrupt */ tegra_uart_handle_modem_signal_change(u); break; - case 1: /* Transmit interrupt only triggered when using PIO */ + case UART_IIR_THRI: /* Transmit interrupt only triggered when using PIO */ tup->ier_shadow &= ~UART_IER_THRI; tegra_uart_write(tup, tup->ier_shadow, UART_IER); tegra_uart_handle_tx_pio(tup); break; - case 4: /* End of data */ - case 6: /* Rx timeout */ - case 2: /* Receive */ + case TEGRA_UART_IIR_EOD: /* End of data */ + case UART_IIR_RX_TIMEOUT: /* Rx timeout */ + case UART_IIR_RDI: /* Receive */ if (!is_rx_int) { is_rx_int = true; /* Disable Rx interrupts */ @@ -734,13 +736,12 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) } break; - case 3: /* Receive error */ + case UART_IIR_RLSI: /* Receive error */ tegra_uart_decode_rx_error(tup, tegra_uart_read(tup, UART_LSR)); break; - case 5: /* break nothing to handle */ - case 7: /* break nothing to handle */ + default: break; } } From 0365eb6cff67981af5ce022e345ae912e15d15de Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Fri, 21 Apr 2017 14:03:18 +0200 Subject: [PATCH 49/57] serial work --- arch/mips/pmcs-msp71xx/msp_serial.c | 2 +- arch/x86/platform/ce4100/ce4100.c | 6 +++--- drivers/bluetooth/btuart_cs.c | 7 +++---- drivers/bluetooth/dtl1_cs.c | 6 +++--- drivers/isdn/hisax/elsa.c | 8 ++++---- drivers/isdn/hisax/elsa_ser.c | 6 +++--- drivers/staging/dgnc/dgnc_cls.h | 6 ------ drivers/staging/dgnc/dgnc_neo.c | 12 ++++++------ drivers/staging/dgnc/dgnc_neo.h | 1 - drivers/tty/mxser.c | 8 ++++---- drivers/tty/mxser.h | 7 ------- drivers/tty/serial/8250/8250_core.c | 5 +++-- drivers/tty/serial/8250/8250_dw.c | 27 ++++++++++++++++++++------- drivers/tty/serial/8250/8250_fsl.c | 3 +-- drivers/tty/serial/8250/8250_omap.c | 2 ++ drivers/tty/serial/8250/8250_port.c | 18 ++++++++++-------- drivers/tty/serial/8250/Makefile | 2 ++ drivers/tty/serial/jsm/jsm.h | 3 --- drivers/tty/serial/jsm/jsm_cls.c | 10 +++++----- drivers/tty/serial/jsm/jsm_neo.c | 22 +++++++++------------- include/linux/serial_core.h | 17 +++++++++++++++++ include/uapi/linux/serial_reg.h | 12 ++++++------ 22 files changed, 102 insertions(+), 88 deletions(-) diff --git a/arch/mips/pmcs-msp71xx/msp_serial.c b/arch/mips/pmcs-msp71xx/msp_serial.c index 8e6e8db8dd5fa4..674403a6465151 100644 --- a/arch/mips/pmcs-msp71xx/msp_serial.c +++ b/arch/mips/pmcs-msp71xx/msp_serial.c @@ -68,7 +68,7 @@ static int msp_serial_handle_irq(struct uart_port *p) if (serial8250_handle_irq(p, iir)) { return 1; - } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { + } else if ((iir & UART_IIR_MASK) == UART_IIR_BUSY) { /* * The DesignWare APB UART has an Busy Detect (0x07) interrupt * meaning an LCR write attempt occurred while the UART was diff --git a/arch/x86/platform/ce4100/ce4100.c b/arch/x86/platform/ce4100/ce4100.c index ce4b06733c0920..2efd9df80e34e5 100644 --- a/arch/x86/platform/ce4100/ce4100.c +++ b/arch/x86/platform/ce4100/ce4100.c @@ -59,8 +59,8 @@ static unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset) if (offset == UART_IIR) { offset = offset << p->regshift; - ret = readl(p->membase + offset); - if (ret & UART_IIR_NO_INT) { + ret = readl(p->membase + offset) & UART_IIR_MASK; + if (ret == UART_IIR_NO_INT) { /* see if the TX interrupt should have really set */ ier = mem_serial_in(p, UART_IER); /* see if the UART's XMIT interrupt is enabled */ @@ -69,7 +69,7 @@ static unsigned int ce4100_mem_serial_in(struct uart_port *p, int offset) /* now check to see if the UART should be generating an interrupt (but isn't) */ if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) - ret &= ~UART_IIR_NO_INT; + ret != UART_IIR_NO_INT; } } } else diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 310e9c2e09b600..e6d985ca32b5bf 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -300,8 +300,8 @@ static irqreturn_t btuart_interrupt(int irq, void *dev_inst) spin_lock(&(info->lock)); - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - while (iir) { + iir = inb(iobase + UART_IIR) & UART_IIR_MASK; + while (iir != UART_IIR_NO_INT) { r = IRQ_HANDLED; /* Clear interrupt */ @@ -330,8 +330,7 @@ static irqreturn_t btuart_interrupt(int irq, void *dev_inst) if (boguscount++ > 100) break; - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - + iir = inb(iobase + UART_IIR) & UART_IIR_MASK; } spin_unlock(&(info->lock)); diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 2adfe4fade7635..ea6df44d2b47c6 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -303,8 +303,8 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) spin_lock(&(info->lock)); - iir = inb(iobase + UART_IIR) & UART_IIR_ID; - while (iir) { + iir = inb(iobase + UART_IIR) & UART_IIR_MASK; + while (iir != UART_IIR_NO_INT) { r = IRQ_HANDLED; /* Clear interrupt */ @@ -333,7 +333,7 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) if (boguscount++ > 100) break; - iir = inb(iobase + UART_IIR) & UART_IIR_ID; + iir = inb(iobase + UART_IIR) & UART_IIR_MASK; } diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index 03bc5d504e2266..6f427313dad628 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -297,8 +297,8 @@ elsa_interrupt(int intno, void *dev_id) spin_lock_irqsave(&cs->lock, flags); #if ARCOFI_USE if (cs->hw.elsa.MFlag) { - val = serial_inp(cs, UART_IIR); - if (!(val & UART_IIR_NO_INT)) { + val = serial_inp(cs, UART_IIR) & UART_IIR_MASK; + if (val != UART_IIR_NO_INT) { debugl1(cs, "IIR %02x", val); rs_interrupt_elsa(cs); } @@ -377,8 +377,8 @@ elsa_interrupt_ipac(int intno, void *dev_id) } #if ARCOFI_USE if (cs->hw.elsa.MFlag) { - val = serial_inp(cs, UART_IIR); - if (!(val & UART_IIR_NO_INT)) { + val = serial_inp(cs, UART_IIR) & UART_IIR_MASK; + if (val != UART_IIR_NO_INT) { debugl1(cs, "IIR %02x", val); rs_interrupt_elsa(cs); } diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index 999effd7a27688..0422647a9b3b5e 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -407,13 +407,13 @@ static void rs_interrupt_elsa(struct IsdnCardState *cs) printk("rs_single loop break.\n"); break; } - iir = serial_inp(cs, UART_IIR); + iir = serial_inp(cs, UART_IIR) & UART_IIR_MASK; debugl1(cs, "rs IIR %02x", iir); - if ((iir & 0xf) == 0) { + if (iir == UART_IIR_MSI) { msr = serial_inp(cs, UART_MSR); debugl1(cs, "rs MSR %02x", msr); } - } while (!(iir & UART_IIR_NO_INT)); + } while (iir != UART_IIR_NO_INT); #ifdef SERIAL_DEBUG_INTR printk("end.\n"); #endif diff --git a/drivers/staging/dgnc/dgnc_cls.h b/drivers/staging/dgnc/dgnc_cls.h index 9dfa9682a8973e..8235687e7fabe4 100644 --- a/drivers/staging/dgnc/dgnc_cls.h +++ b/drivers/staging/dgnc/dgnc_cls.h @@ -52,12 +52,6 @@ struct cls_uart_struct { #define UART_16654_FCR_RXTRIGGER_16 0x40 #define UART_16654_FCR_RXTRIGGER_56 0x80 -/* Received CTS/RTS change of state */ -#define UART_IIR_CTSRTS 0x20 - -/* Receiver data TIMEOUT */ -#define UART_IIR_RDI_TIMEOUT 0x0C - /* * These are the EXTENDED definitions for the Exar 654's Interrupt * Enable Register. diff --git a/drivers/staging/dgnc/dgnc_neo.c b/drivers/staging/dgnc/dgnc_neo.c index 1943e66fec571c..2af883e1336efa 100644 --- a/drivers/staging/dgnc/dgnc_neo.c +++ b/drivers/staging/dgnc/dgnc_neo.c @@ -418,7 +418,7 @@ static inline void neo_parse_isr(struct dgnc_board *brd, uint port) neo_copy_data_from_queue_to_uart(ch); } - if (isr & UART_17158_IIR_XONXOFF) { + if (isr == UART_17158_IIR_XONXOFF) { cause = readb(&ch->ch_neo_uart->xoffchar1); /* @@ -447,7 +447,7 @@ static inline void neo_parse_isr(struct dgnc_board *brd, uint port) } } - if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) { + if (isr == UART_17158_IIR_HWFLOW_STATE_CHANGE) { /* * If we get here, this means the hardware is * doing auto flow control. Check to see whether @@ -1228,8 +1228,8 @@ static void neo_flush_uart_write(struct channel_t *ch) * Check to see if the UART completely flushed the FIFO * FIFO. */ - tmp = readb(&ch->ch_neo_uart->isr_fcr); - if (tmp & 4) + tmp = readb(&ch->ch_neo_uart->isr_fcr) & UART_IIR_MASK; + if (tmp == UART_IIR_RDI) udelay(10); else break; @@ -1259,8 +1259,8 @@ static void neo_flush_uart_read(struct channel_t *ch) * Check to see if the UART feels it completely flushed the * FIFO. */ - tmp = readb(&ch->ch_neo_uart->isr_fcr); - if (tmp & 2) + tmp = readb(&ch->ch_neo_uart->isr_fcr) & UART_IIR_MASK; + if (tmp == UART_IIR_THRI) udelay(10); else break; diff --git a/drivers/staging/dgnc/dgnc_neo.h b/drivers/staging/dgnc/dgnc_neo.h index c30a2c2b5eea95..aa7d0fe14b1f01 100644 --- a/drivers/staging/dgnc/dgnc_neo.h +++ b/drivers/staging/dgnc/dgnc_neo.h @@ -125,7 +125,6 @@ struct neo_uart_struct { #define UART_17158_RX_FIFOSIZE 64 #define UART_17158_TX_FIFOSIZE 64 -/* 17158 Extended IIR's */ #define UART_17158_IIR_RDI_TIMEOUT 0x0C /* Receiver data TIMEOUT */ #define UART_17158_IIR_XONXOFF 0x10 /* Received an XON/XOFF char */ #define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20 /* CTS/DSR or RTS/DTR diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c index 7dd38047ba2352..badda54d1f04c9 100644 --- a/drivers/tty/mxser.c +++ b/drivers/tty/mxser.c @@ -2248,10 +2248,10 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) int_cnt = 0; spin_lock(&port->slock); do { - iir = inb(port->ioaddr + UART_IIR); - if (iir & UART_IIR_NO_INT) + iir = inb(port->ioaddr + UART_IIR) & + UART_IIR_MASK | UART_IIR_EXT_MASK; + if (iir == UART_IIR_NO_INT) break; - iir &= MOXA_MUST_IIR_MASK; tty = tty_port_tty_get(&port->port); if (!tty || port->closing || !tty_port_initialized(&port->port)) { @@ -2293,7 +2293,7 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) mxser_check_modem_status(tty, port, msr); if (port->board->chip_flag) { - if (iir == 0x02 && (status & + if (iir == UART_IIR_THRI && (status & UART_LSR_THRE)) mxser_transmit_chars(tty, port); } else { diff --git a/drivers/tty/mxser.h b/drivers/tty/mxser.h index 0bf794313ffd5c..afe300218f90e1 100644 --- a/drivers/tty/mxser.h +++ b/drivers/tty/mxser.h @@ -113,13 +113,6 @@ #define MOXA_MUST_IIR_RTO 0x0C #define MOXA_MUST_IIR_LSR 0x06 -/* received Xon/Xoff or specical interrupt pending */ -#define MOXA_MUST_IIR_XSC 0x10 - -/* RTS/CTS change state interrupt pending */ -#define MOXA_MUST_IIR_RTSCTS 0x20 -#define MOXA_MUST_IIR_MASK 0x3E - #define MOXA_MUST_MCR_XON_FLAG 0x40 #define MOXA_MUST_MCR_XON_ANY 0x80 #define MOXA_MUST_MCR_TX_XON 0x08 diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 9d83b0dbe5a15b..d821709eef22e7 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -110,7 +111,7 @@ static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */ */ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) { - struct irq_info *i = dev_id; + struct irq_info *i = (struct irq_info *)dev_id; struct list_head *l, *end = NULL; int pass_counter = 0, handled = 0; @@ -137,7 +138,7 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) if (l == i->head && pass_counter++ > PASS_LIMIT) { /* If we hit this, we're dead. */ printk_ratelimited(KERN_ERR - "serial8250: too much work for irq%d\n", irq); + "serial8250: too much work for irq%d (%p)\n", irq, port->membase); break; } } while (l != end); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index c7b4ca0949280a..feac009eb61042 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -35,8 +35,14 @@ /* Offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ -#define DW_UART_CPR 0xf4 /* Component Parameter Register */ -#define DW_UART_UCV 0xf8 /* UART Component Version */ +#define DW_UART_CPR 0x3d /* Component Parameter Register */ +#define DW_UART_UCV 0x3e /* UART Component Version */ + +/* Offsets for the specific registers */ +#define OCTEON_UART_USR 0x27 /* UART Status Register */ + +/* UART Status Register bits */ +#define DW_UART_USR_BUSY BIT(0) /* Component Parameter Register bits */ #define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0) @@ -99,11 +105,16 @@ static void dw8250_check_lcr(struct uart_port *p, int value) /* Make sure LCR write wasn't ignored */ while (tries--) { + struct dw8250_data *d = p->private_data; + unsigned int usr = serial_port_in(p, d->usr_reg); unsigned int lcr = p->serial_in(p, UART_LCR); if ((value & ~UART_LCR_SPAR) == (lcr & ~UART_LCR_SPAR)) return; + if (lcr & DW_UART_USR_BUSY) + continue; + dw8250_force_idle(p); #ifdef CONFIG_64BIT @@ -335,7 +346,7 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->serial_out = dw8250_serial_outq; p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; p->type = PORT_OCTEON; - data->usr_reg = 0x27; + data->usr_reg = OCTEON_UART_USR; data->skip_autocfg = true; } #endif @@ -376,19 +387,21 @@ static void dw8250_setup_port(struct uart_port *p) * ADDITIONAL_FEATURES are not enabled. No need to go any further. */ if (p->iotype == UPIO_MEM32BE) - reg = ioread32be(p->membase + DW_UART_UCV); + reg = ioread32be(p->membase + (DW_UART_UCV << p->regshift)); else - reg = readl(p->membase + DW_UART_UCV); + reg = readl(p->membase + (DW_UART_UCV << p->regshift)); if (!reg) return; + printk("Designware UART version %c.%c%c\n", + (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); dev_dbg(p->dev, "Designware UART version %c.%c%c\n", (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); if (p->iotype == UPIO_MEM32BE) - reg = ioread32be(p->membase + DW_UART_CPR); + reg = ioread32be(p->membase + (DW_UART_CPR << p->regshift)); else - reg = readl(p->membase + DW_UART_CPR); + reg = readl(p->membase + (DW_UART_CPR << p->regshift)); if (!reg) return; diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index b866343bb485ca..a4eaa6e44d395f 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -32,8 +32,7 @@ int fsl8250_handle_irq(struct uart_port *port) spin_lock_irqsave(&up->port.lock, flags); - iir = port->serial_in(port, UART_IIR); - iir &= UART_IIR_MASK; + iir = port->serial_in(port, UART_IIR) & UART_IIR_MASK; if (iir == UART_IIR_NO_INT) { spin_unlock_irqrestore(&up->port.lock, flags); return 0; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index f9ab3aad4579f8..2c4a39ef93f0c5 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1025,6 +1025,8 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) case UART_IIR_RDI: omap_8250_rx_dma_flush(up); return true; + default: + break; } return omap_8250_rx_dma(up); } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 8af9cbb057162b..80f240920400a9 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1842,6 +1842,8 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) /* fall-through */ case UART_IIR_RLSI: return true; + default: + break; } return up->dma->rx_dma(up); } @@ -1919,10 +1921,10 @@ static int exar_handle_irq(struct uart_port *port) static int serial8250_tx_threshold_handle_irq(struct uart_port *port) { unsigned long flags; - unsigned int iir = serial_port_in(port, UART_IIR); + unsigned int iir = serial_port_in(port, UART_IIR) & UART_IIR_MASK; /* TX Threshold IRQ triggered so load up FIFO */ - if ((iir & UART_IIR_MASK) == UART_IIR_THRI) { + if (iir == UART_IIR_THRI) { struct uart_8250_port *up = up_to_u8250p(port); spin_lock_irqsave(&port->lock, flags); @@ -2274,11 +2276,11 @@ int serial8250_do_startup(struct uart_port *port) wait_for_xmitr(up, UART_LSR_THRE); serial_port_out_sync(port, UART_IER, UART_IER_THRI); udelay(1); /* allow THRE to set */ - iir1 = serial_port_in(port, UART_IIR); + iir1 = serial_port_in(port, UART_IIR) & UART_IIR_MASK; serial_port_out(port, UART_IER, 0); serial_port_out_sync(port, UART_IER, UART_IER_THRI); udelay(1); /* allow a working UART time to re-assert THRE */ - iir = serial_port_in(port, UART_IIR); + iir = serial_port_in(port, UART_IIR) & UART_IIR_MASK; serial_port_out(port, UART_IER, 0); if (port->irqflags & IRQF_SHARED) @@ -2290,8 +2292,8 @@ int serial8250_do_startup(struct uart_port *port) * don't trust the iir, setup a timer to kick the UART * on a regular basis. */ - if ((((iir1 & UART_IIR_MASK) != UART_IIR_NO_INT) && - ((iir & UART_IIR_MASK) == UART_IIR_NO_INT)) || + if (((iir1 != UART_IIR_NO_INT) && + (iir == UART_IIR_NO_INT)) || up->port.flags & UPF_BUG_THRE) { up->bugs |= UART_BUG_THRE; } @@ -2339,10 +2341,10 @@ int serial8250_do_startup(struct uart_port *port) */ serial_port_out(port, UART_IER, UART_IER_THRI); lsr = serial_port_in(port, UART_LSR); - iir = serial_port_in(port, UART_IIR); + iir = serial_port_in(port, UART_IIR) & UART_IIR_MASK; serial_port_out(port, UART_IER, 0); - if (lsr & UART_LSR_TEMT && ((iir & UART_IIR_MASK) == UART_IIR_NO_INT)) { + if (lsr & UART_LSR_TEMT && (iir == UART_IIR_NO_INT)) { if (!(up->bugs & UART_BUG_TXEN)) { up->bugs |= UART_BUG_TXEN; pr_debug("ttyS%d - enabling bad tx status workarounds\n", diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 6a18d2d768fe69..ccc48234592c0b 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -38,3 +38,5 @@ obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt + +CFLAGS_8250_dw.o += -DDEBUG diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h index 0b79b87df47d70..ae583af0a5bc1d 100644 --- a/drivers/tty/serial/jsm/jsm.h +++ b/drivers/tty/serial/jsm/jsm.h @@ -291,9 +291,6 @@ struct cls_uart_struct { #define UART_16654_FCR_RXTRIGGER_56 0x80 #define UART_16654_FCR_RXTRIGGER_60 0xC0 -#define UART_IIR_CTSRTS 0x20 /* Received CTS/RTS change of state */ -#define UART_IIR_RDI_TIMEOUT 0x0C /* Receiver data TIMEOUT */ - /* * These are the EXTENDED definitions for the Exar 654's Interrupt * Enable Register. diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c index 4eb12a9cae76ce..74893a055046d7 100644 --- a/drivers/tty/serial/jsm/jsm_cls.c +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -579,21 +579,21 @@ static inline void cls_parse_isr(struct jsm_board *brd, uint port) /* Here we try to figure out what caused the interrupt to happen */ while (1) { - isr = readb(&ch->ch_cls_uart->isr_fcr); + isr = readb(&ch->ch_cls_uart->isr_fcr) & UART_IIR_MASK; /* Bail if no pending interrupt on port */ - if (isr & UART_IIR_NO_INT) + if (isr == UART_IIR_NO_INT) break; /* Receive Interrupt pending */ - if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) { + if ((isr == UART_IIR_RDI) || (isr == UART_IIR_RX_TIMEOUT)) { /* Read data from uart -> queue */ cls_copy_data_from_uart_to_queue(ch); jsm_check_queue_flow_control(ch); } /* Transmit Hold register empty pending */ - if (isr & UART_IIR_THRI) { + if (isr == UART_IIR_THRI) { /* Transfer data (if any) from Write Queue -> UART. */ spin_lock_irqsave(&ch->ch_lock, flags); ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); @@ -626,7 +626,7 @@ static void cls_flush_uart_write(struct jsm_channel *ch) for (i = 0; i < 10; i++) { /* Check to see if the UART feels it completely flushed FIFO */ - tmp = readb(&ch->ch_cls_uart->isr_fcr); + tmp = readb(&ch->ch_cls_uart->isr_fcr) & UART_IIR_MASK; if (tmp & UART_FCR_CLEAR_XMIT) { jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "Still flushing TX UART... i: %d\n", i); diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c index c6fdd6369534c0..9ba7287cd8d964 100644 --- a/drivers/tty/serial/jsm/jsm_neo.c +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -675,8 +675,8 @@ static void neo_flush_uart_read(struct jsm_channel *ch) for (i = 0; i < 10; i++) { /* Check to see if the UART feels it completely flushed the FIFO. */ - tmp = readb(&ch->ch_neo_uart->isr_fcr); - if (tmp & 2) { + tmp = readb(&ch->ch_neo_uart->isr_fcr) & UART_IIR_MASK; + if (tmp == UART_IIR_THRI) { jsm_dbg(IOCTL, &ch->ch_bd->pci_dev, "Still flushing RX UART... i: %d\n", i); udelay(10); @@ -734,21 +734,17 @@ static void neo_parse_isr(struct jsm_board *brd, u32 port) /* Here we try to figure out what caused the interrupt to happen */ while (1) { - isr = readb(&ch->ch_neo_uart->isr_fcr); + isr = readb(&ch->ch_neo_uart->isr_fcr) & + (UART_IIR_MASK | UART_IIR_EXT_MASK); /* Bail if no pending interrupt */ - if (isr & UART_IIR_NO_INT) + if (isr == UART_IIR_NO_INT) break; - /* - * Yank off the upper 2 bits, which just show that the FIFO's are enabled. - */ - isr &= ~(UART_17158_IIR_FIFO_ENABLED); - jsm_dbg(INTR, &ch->ch_bd->pci_dev, "%s:%d isr: %x\n", __FILE__, __LINE__, isr); - if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) { + if ((isr == UART_IIR_RX_TIMEOUT) || (isr == UART_IIR_RDI)) { /* Read data from uart -> queue */ neo_copy_data_from_uart_to_queue(ch); @@ -758,7 +754,7 @@ static void neo_parse_isr(struct jsm_board *brd, u32 port) spin_unlock_irqrestore(&ch->ch_lock, lock_flags); } - if (isr & UART_IIR_THRI) { + if (isr == UART_IIR_THRI) { /* Transfer data (if any) from Write Queue -> UART. */ spin_lock_irqsave(&ch->ch_lock, lock_flags); ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); @@ -766,7 +762,7 @@ static void neo_parse_isr(struct jsm_board *brd, u32 port) neo_copy_data_from_queue_to_uart(ch); } - if (isr & UART_17158_IIR_XONXOFF) { + if (isr == UART_IIR_XOFF) { cause = readb(&ch->ch_neo_uart->xoffchar1); jsm_dbg(INTR, &ch->ch_bd->pci_dev, @@ -801,7 +797,7 @@ static void neo_parse_isr(struct jsm_board *brd, u32 port) spin_unlock_irqrestore(&ch->ch_lock, lock_flags); } - if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) { + if (isr == UART_IIR_CTS_RTS_DSR) { /* * If we get here, this means the hardware is doing auto flow control. * Check to see whether RTS/DTR or CTS/DSR caused this interrupt. diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 5553e04e59c9fb..177e145c5feea9 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef CONFIG_SERIAL_CORE_CONSOLE #define uart_console(port) \ @@ -265,6 +266,22 @@ static inline void serial_port_out(struct uart_port *up, int offset, int value) up->serial_out(up, offset, value); } +/** + * serial_port_in_IIR - Helper function to obtain the interrupt ID from the + * Interrupt ID Register. + * @up: UART port to get the intterupt ID from + * @mask: Additional mask to filter against + */ +static inline int serial_port_in_IIR_MASK(struct uart_port *up, int mask) +{ + return serial_port_in(up, UART_IIR) & (UART_IIR_MASK | mask); +} + +static inline int serial_port_in_IIR(struct uart_port *up) +{ + return serial_port_in_IIR_MASK(up, 0); +} + /** * enum uart_pm_state - power states for UARTs * @UART_PM_STATE_ON: UART is powered, up and operational diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 489522389a10c2..88f251fb552b5f 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -36,13 +36,13 @@ #define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ #define UART_IIR_RDI 0x04 /* Receiver data interrupt */ #define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ -#define UART_IIR_BUSY 0x07 /* DesignWare APB Busy Detect */ -#define UART_IIR_RX_TIMEOUT 0x0c /* DesignWare RX Timeout interrupt */ -#define UART_IIR_MASK 0x0f /* DesignWare IIR mask */ +#define UART_IIR_BUSY 0x07 /* Busy Detect */ +#define UART_IIR_RX_TIMEOUT 0x0c /* RX Timeout interrupt */ +#define UART_IIR_MASK 0x0f /* Generic IIR mask */ -#define UART_IIR_XOFF 0x10 /* OMAP XOFF/Special Character */ -#define UART_IIR_CTS_RTS_DSR 0x20 /* OMAP CTS/RTS/DSR Change */ -#define UART_IIR_EXT_MASK 0x30 /* OMAP extended IIR mask */ +#define UART_IIR_XOFF 0x10 /* XOFF/Special Character */ +#define UART_IIR_CTS_RTS_DSR 0x20 /* CTS/RTS/DSR Change */ +#define UART_IIR_EXT_MASK 0x30 /* Extended IIR mask */ #define UART_FCR 2 /* Out: FIFO Control Register */ #define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ From 7d6eb22f76fb9d0902fcb3ee9e0009bc7373bf80 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Mon, 4 Sep 2017 10:03:43 +0200 Subject: [PATCH 50/57] drm/sun4i: i2c: consolidate ddc items The ddc items can be consolidated more into one place. Signed-off-by: Olliver Schinagl --- drivers/gpu/drm/sun4i/sun4i_hdmi.h | 134 ------- drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c | 1 + drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 44 +-- drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c | 404 ++++++++++++++------- drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h | 114 ++++++ 5 files changed, 381 insertions(+), 316 deletions(-) create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h b/drivers/gpu/drm/sun4i/sun4i_hdmi.h index b685ee11623d10..d556a0b6fe0e73 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h @@ -108,90 +108,6 @@ #define SUN4I_HDMI_UNKNOWN_REG 0x300 #define SUN4I_HDMI_UNKNOWN_INPUT_SYNC BIT(27) -#define SUN4I_HDMI_DDC_CTRL_REG 0x500 -#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31) -#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE (1 << 8) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8) -#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0) - -#define SUN4I_HDMI_DDC_ADDR_REG 0x504 -#define SUN4I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) -#define SUN4I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) -#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) -#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & 0xff) - -#define SUN4I_HDMI_DDC_INT_STATUS_REG 0x50c -#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION BIT(7) -#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW BIT(6) -#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW BIT(5) -#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST BIT(4) -#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR BIT(3) -#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR BIT(2) -#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR BIT(1) -#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE BIT(0) - -#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x510 -#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n) (((n) & 0xf) << 4) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX (BIT(4) - 1) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n) ((n) & 0xf) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX (BIT(4) - 1) - -#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x518 - -#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x51c -#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX (BIT(10) - 1) - -#define SUN4I_HDMI_DDC_CMD_REG 0x520 -#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 6 -#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ 5 -#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE 3 - -#define SUN4I_HDMI_DDC_CLK_REG 0x528 -#define SUN4I_HDMI_DDC_CLK_M(m) (((m) & 0x7) << 3) -#define SUN4I_HDMI_DDC_CLK_N(n) ((n) & 0x7) - -#define SUN4I_HDMI_DDC_LINE_CTRL_REG 0x540 -#define SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE BIT(9) -#define SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE BIT(8) - -#define SUN4I_HDMI_DDC_FIFO_SIZE 16 - -/* A31 specific */ -#define SUN6I_HDMI_DDC_CTRL_REG 0x500 -#define SUN6I_HDMI_DDC_CTRL_RESET BIT(31) -#define SUN6I_HDMI_DDC_CTRL_START_CMD BIT(27) -#define SUN6I_HDMI_DDC_CTRL_SDA_ENABLE BIT(6) -#define SUN6I_HDMI_DDC_CTRL_SCL_ENABLE BIT(4) -#define SUN6I_HDMI_DDC_CTRL_ENABLE BIT(0) - -#define SUN6I_HDMI_DDC_CMD_REG 0x508 -#define SUN6I_HDMI_DDC_CMD_BYTE_COUNT(count) ((count) << 16) -/* command types in lower 3 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_ADDR_REG 0x50c -#define SUN6I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) -#define SUN6I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) -#define SUN6I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) -#define SUN6I_HDMI_DDC_ADDR_SLAVE(addr) (((addr) & 0xff) << 1) - -#define SUN6I_HDMI_DDC_INT_STATUS_REG 0x514 -#define SUN6I_HDMI_DDC_INT_STATUS_TIMEOUT BIT(8) -/* lower 8 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_FIFO_CTRL_REG 0x518 -#define SUN6I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(15) -/* lower 9 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_CLK_REG 0x520 -/* DDC CLK bit fields are the same, but the formula is not */ - -#define SUN6I_HDMI_DDC_FIFO_DATA_REG 0x580 - enum sun4i_hdmi_pkt_type { SUN4I_HDMI_PKT_AVI = 2, SUN4I_HDMI_PKT_END = 15, @@ -210,40 +126,6 @@ struct sun4i_hdmi_variant { u8 ddc_clk_m_offset; u8 tmds_clk_div_offset; - - /* Register fields for I2C adapter */ - struct reg_field field_ddc_en; - struct reg_field field_ddc_start; - struct reg_field field_ddc_reset; - struct reg_field field_ddc_addr_reg; - struct reg_field field_ddc_slave_addr; - struct reg_field field_ddc_int_mask; - struct reg_field field_ddc_int_status; - struct reg_field field_ddc_fifo_clear; - struct reg_field field_ddc_fifo_rx_thres; - struct reg_field field_ddc_fifo_tx_thres; - struct reg_field field_ddc_byte_count; - struct reg_field field_ddc_cmd; - struct reg_field field_ddc_sda_en; - struct reg_field field_ddc_sck_en; - - /* DDC FIFO register offset */ - u32 ddc_fifo_reg; - - /* - * DDC FIFO threshold boundary conditions - * - * This is used to cope with the threshold boundary condition - * being slightly different on sun5i and sun6i. - * - * On sun5i the threshold is exclusive, i.e. does not include, - * the value of the threshold. ( > for RX; < for TX ) - * On sun6i the threshold is inclusive, i.e. includes, the - * value of the threshold. ( >= for RX; <= for TX ) - */ - bool ddc_fifo_thres_incl; - - bool ddc_fifo_has_dir; }; struct sun4i_hdmi { @@ -270,22 +152,6 @@ struct sun4i_hdmi { struct i2c_adapter *i2c; - /* Regmap fields for I2C adapter */ - struct regmap_field *field_ddc_en; - struct regmap_field *field_ddc_start; - struct regmap_field *field_ddc_reset; - struct regmap_field *field_ddc_addr_reg; - struct regmap_field *field_ddc_slave_addr; - struct regmap_field *field_ddc_int_mask; - struct regmap_field *field_ddc_int_status; - struct regmap_field *field_ddc_fifo_clear; - struct regmap_field *field_ddc_fifo_rx_thres; - struct regmap_field *field_ddc_fifo_tx_thres; - struct regmap_field *field_ddc_byte_count; - struct regmap_field *field_ddc_cmd; - struct regmap_field *field_ddc_sda_en; - struct regmap_field *field_ddc_sck_en; - struct sun4i_drv *drv; bool hdmi_monitor; diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c index e826da34e9191f..8cd6ec142fe48e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c @@ -14,6 +14,7 @@ #include #include "sun4i_hdmi.h" +#include "sun4i_hdmi_i2c.h" struct sun4i_ddc { struct clk_hw hw; diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index dda904ec0534cd..4242e4a520b1c1 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -30,6 +30,8 @@ #include "sun4i_crtc.h" #include "sun4i_drv.h" #include "sun4i_hdmi.h" +#include "sun4i_hdmi_i2c.h" +#include "sun4i_tcon.h" static inline struct sun4i_hdmi * drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder) @@ -354,27 +356,6 @@ static const struct sun4i_hdmi_variant sun5i_variant = { SUN4I_HDMI_PLL_CTRL_HV_IS_33 | SUN4I_HDMI_PLL_CTRL_BWS | SUN4I_HDMI_PLL_CTRL_PLL_EN, - - .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), - .ddc_clk_pre_divider = 2, - .ddc_clk_m_offset = 1, - - .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), - .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), - .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), - .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), - .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), - .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), - .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), - .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), - .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), - .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), - .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), - .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), - .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), - - .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, - .ddc_fifo_has_dir = true, }; static const struct sun4i_hdmi_variant sun6i_variant = { @@ -410,28 +391,7 @@ static const struct sun4i_hdmi_variant sun6i_variant = { SUN4I_HDMI_PLL_CTRL_HV_IS_33 | SUN4I_HDMI_PLL_CTRL_PLL_EN, - .ddc_clk_reg = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG, 0, 6), - .ddc_clk_pre_divider = 1, - .ddc_clk_m_offset = 2, - .tmds_clk_div_offset = 1, - - .field_ddc_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 0, 0), - .field_ddc_start = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 27, 27), - .field_ddc_reset = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 31, 31), - .field_ddc_addr_reg = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 31), - .field_ddc_slave_addr = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 7), - .field_ddc_int_status = REG_FIELD(SUN6I_HDMI_DDC_INT_STATUS_REG, 0, 8), - .field_ddc_fifo_clear = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 18, 18), - .field_ddc_fifo_rx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), - .field_ddc_fifo_tx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), - .field_ddc_byte_count = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 16, 25), - .field_ddc_cmd = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 0, 2), - .field_ddc_sda_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 6, 6), - .field_ddc_sck_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 4, 4), - - .ddc_fifo_reg = SUN6I_HDMI_DDC_FIFO_DATA_REG, - .ddc_fifo_thres_incl = true, }; static const struct regmap_config sun4i_hdmi_regmap_config = { diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c index 58e9d37e8c17c0..24cd4251b72f74 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c @@ -13,20 +13,126 @@ #include #include "sun4i_hdmi.h" - -#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \ - SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \ - SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \ - SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \ - SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \ - SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \ - SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \ -) +#include "sun4i_hdmi_i2c.h" /* FIFO request bit is set when FIFO level is above RX_THRESHOLD during read */ #define RX_THRESHOLD SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX -static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, int len, bool read) +struct sun4i_hdmi_i2c_variant { + struct reg_field ddc_clk_reg; + u8 ddc_clk_pre_divider; + u8 ddc_clk_m_offset; + + /* Register fields for I2C adapter */ + struct reg_field field_ddc_en; + struct reg_field field_ddc_start; + struct reg_field field_ddc_reset; + struct reg_field field_ddc_addr_reg; + struct reg_field field_ddc_slave_addr; + struct reg_field field_ddc_int_mask; + struct reg_field field_ddc_int_status; + struct reg_field field_ddc_fifo_clear; + struct reg_field field_ddc_fifo_rx_thres; + struct reg_field field_ddc_fifo_tx_thres; + struct reg_field field_ddc_byte_count; + struct reg_field field_ddc_cmd; + struct reg_field field_ddc_sda_en; + struct reg_field field_ddc_sck_en; + + /* DDC FIFO register offset */ + u32 ddc_fifo_reg; + + /* + * DDC FIFO threshold boundary conditions + * + * This is used to cope with the threshold boundary condition + * being slightly different on sun5i and sun6i. + * + * On sun5i the threshold is exclusive, i.e. does not include, + * the value of the threshold. ( > for RX; < for TX ) + * On sun6i the threshold is inclusive, i.e. includes, the + * value of the threshold. ( >= for RX; <= for TX ) + */ + bool ddc_fifo_thres_incl; + + bool ddc_fifo_has_dir; +}; + +struct sun4i_hdmi_i2c_drv { + struct device *dev; + + void __iomem *base; + struct regmap *regmap; + + struct clk *ddc_clk; + + struct i2c_adapter adap; + + struct regmap_field *field_ddc_en; + struct regmap_field *field_ddc_start; + struct regmap_field *field_ddc_reset; + struct regmap_field *field_ddc_addr_reg; + struct regmap_field *field_ddc_slave_addr; + struct regmap_field *field_ddc_int_mask; + struct regmap_field *field_ddc_int_status; + struct regmap_field *field_ddc_fifo_clear; + struct regmap_field *field_ddc_fifo_rx_thres; + struct regmap_field *field_ddc_fifo_tx_thres; + struct regmap_field *field_ddc_byte_count; + struct regmap_field *field_ddc_cmd; + struct regmap_field *field_ddc_sda_en; + struct regmap_field *field_ddc_sck_en; + + const struct sun4i_hdmi_i2c_variant *variant; +}; + +static const struct sun4i_hdmi_i2c_variant sun5i_variant = { + .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), + .ddc_clk_pre_divider = 2, + .ddc_clk_m_offset = 1, + + .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), + .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), + .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), + .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), + .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), + .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), + .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), + .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), + .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), + .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), + .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), + .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), + .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), + + .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, + .ddc_fifo_has_dir = true, +}; + +static const struct sun4i_hdmi_i2c_variant sun6i_variant = { + .ddc_clk_reg = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG, 0, 6), + .ddc_clk_pre_divider = 1, + .ddc_clk_m_offset = 2, + + .field_ddc_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 0, 0), + .field_ddc_start = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 27, 27), + .field_ddc_reset = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 31, 31), + .field_ddc_addr_reg = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 31), + .field_ddc_slave_addr = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 7), + .field_ddc_int_status = REG_FIELD(SUN6I_HDMI_DDC_INT_STATUS_REG, 0, 8), + .field_ddc_fifo_clear = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 18, 18), + .field_ddc_fifo_rx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), + .field_ddc_fifo_tx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), + .field_ddc_byte_count = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 16, 25), + .field_ddc_cmd = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 0, 2), + .field_ddc_sda_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 6, 6), + .field_ddc_sck_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 4, 4), + + .ddc_fifo_reg = SUN6I_HDMI_DDC_FIFO_DATA_REG, + .ddc_fifo_thres_incl = true, +}; + +static int fifo_transfer(struct sun4i_hdmi_i2c_drv *drv, u8 *buf, int len, bool read) { /* * 1 byte takes 9 clock cycles (8 bits + 1 ACK) = 90 us for 100 kHz @@ -42,7 +148,7 @@ static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, int len, bool read) * RX_THRESHOLD number of bytes, instead of RX_THRESHOLD + 1. */ int read_len = RX_THRESHOLD + - (hdmi->variant->ddc_fifo_thres_incl ? 0 : 1); + (drv->variant->ddc_fifo_thres_incl ? 0 : 1); /* * Limit transfer length by FIFO threshold or FIFO size. @@ -51,7 +157,7 @@ static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, int len, bool read) len = min_t(int, len, read ? read_len : SUN4I_HDMI_DDC_FIFO_SIZE); /* Wait until error, FIFO request bit set or transfer complete */ - if (regmap_field_read_poll_timeout(hdmi->field_ddc_int_status, reg, + if (regmap_field_read_poll_timeout(drv->field_ddc_int_status, reg, reg & mask, len * byte_time_ns, 100000)) return -ETIMEDOUT; @@ -60,37 +166,37 @@ static int fifo_transfer(struct sun4i_hdmi *hdmi, u8 *buf, int len, bool read) return -EIO; if (read) - readsb(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len); + readsb(drv->base + drv->variant->ddc_fifo_reg, buf, len); else - writesb(hdmi->base + hdmi->variant->ddc_fifo_reg, buf, len); + writesb(drv->base + drv->variant->ddc_fifo_reg, buf, len); /* Clear FIFO request bit by forcing a write to that bit */ - regmap_field_force_write(hdmi->field_ddc_int_status, + regmap_field_force_write(drv->field_ddc_int_status, SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST); return len; } -static int xfer_msg(struct sun4i_hdmi *hdmi, struct i2c_msg *msg) +static int xfer_msg(struct sun4i_hdmi_i2c_drv *drv, struct i2c_msg *msg) { int i, len; u32 reg; /* Set FIFO direction */ - if (hdmi->variant->ddc_fifo_has_dir) { - reg = readl(hdmi->base + SUN4I_HDMI_DDC_CTRL_REG); + if (drv->variant->ddc_fifo_has_dir) { + reg = readl(drv->base + SUN4I_HDMI_DDC_CTRL_REG); reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK; reg |= (msg->flags & I2C_M_RD) ? SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ : SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE; - writel(reg, hdmi->base + SUN4I_HDMI_DDC_CTRL_REG); + writel(reg, drv->base + SUN4I_HDMI_DDC_CTRL_REG); } /* Clear address register (not cleared by soft reset) */ - regmap_field_write(hdmi->field_ddc_addr_reg, 0); + regmap_field_write(drv->field_ddc_addr_reg, 0); /* Set I2C address */ - regmap_field_write(hdmi->field_ddc_slave_addr, msg->addr); + regmap_field_write(drv->field_ddc_slave_addr, msg->addr); /* * Set FIFO RX/TX thresholds and clear FIFO @@ -98,47 +204,47 @@ static int xfer_msg(struct sun4i_hdmi *hdmi, struct i2c_msg *msg) * If threshold is inclusive, we can set the TX threshold to * 0 instead of 1. */ - regmap_field_write(hdmi->field_ddc_fifo_tx_thres, - hdmi->variant->ddc_fifo_thres_incl ? 0 : 1); - regmap_field_write(hdmi->field_ddc_fifo_rx_thres, RX_THRESHOLD); - regmap_field_write(hdmi->field_ddc_fifo_clear, 1); - if (regmap_field_read_poll_timeout(hdmi->field_ddc_fifo_clear, + regmap_field_write(drv->field_ddc_fifo_tx_thres, + drv->variant->ddc_fifo_thres_incl ? 0 : 1); + regmap_field_write(drv->field_ddc_fifo_rx_thres, RX_THRESHOLD); + regmap_field_write(drv->field_ddc_fifo_clear, 1); + if (regmap_field_read_poll_timeout(drv->field_ddc_fifo_clear, reg, !reg, 100, 2000)) return -EIO; /* Set transfer length */ - regmap_field_write(hdmi->field_ddc_byte_count, msg->len); + regmap_field_write(drv->field_ddc_byte_count, msg->len); /* Set command */ - regmap_field_write(hdmi->field_ddc_cmd, + regmap_field_write(drv->field_ddc_cmd, msg->flags & I2C_M_RD ? SUN4I_HDMI_DDC_CMD_IMPLICIT_READ : SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE); /* Clear interrupt status bits by forcing a write */ - regmap_field_force_write(hdmi->field_ddc_int_status, + regmap_field_force_write(drv->field_ddc_int_status, SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE); /* Start command */ - regmap_field_write(hdmi->field_ddc_start, 1); + regmap_field_write(drv->field_ddc_start, 1); /* Transfer bytes */ for (i = 0; i < msg->len; i += len) { - len = fifo_transfer(hdmi, msg->buf + i, msg->len - i, + len = fifo_transfer(drv, msg->buf + i, msg->len - i, msg->flags & I2C_M_RD); if (len <= 0) return len; } /* Wait for command to finish */ - if (regmap_field_read_poll_timeout(hdmi->field_ddc_start, + if (regmap_field_read_poll_timeout(drv->field_ddc_start, reg, !reg, 100, 100000)) return -EIO; /* Check for errors */ - regmap_field_read(hdmi->field_ddc_int_status, ®); + regmap_field_read(drv->field_ddc_int_status, ®); if ((reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) || !(reg & SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE)) { return -EIO; @@ -150,7 +256,7 @@ static int xfer_msg(struct sun4i_hdmi *hdmi, struct i2c_msg *msg) static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { - struct sun4i_hdmi *hdmi = i2c_get_adapdata(adap); + struct sun4i_hdmi_i2c_drv *drv = i2c_get_adapdata(adap); u32 reg; int err, i, ret = num; @@ -162,30 +268,30 @@ static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap, } /* DDC clock needs to be enabled for the module to work */ - clk_prepare_enable(hdmi->ddc_clk); - clk_set_rate(hdmi->ddc_clk, 100000); + clk_prepare_enable(drv->ddc_clk); + clk_set_rate(drv->ddc_clk, 100000); /* Reset I2C controller */ - regmap_field_write(hdmi->field_ddc_en, 1); - regmap_field_write(hdmi->field_ddc_reset, 1); - if (regmap_field_read_poll_timeout(hdmi->field_ddc_reset, + regmap_field_write(drv->field_ddc_en, 1); + regmap_field_write(drv->field_ddc_reset, 1); + if (regmap_field_read_poll_timeout(drv->field_ddc_reset, reg, !reg, 100, 2000)) { - clk_disable_unprepare(hdmi->ddc_clk); + clk_disable_unprepare(drv->ddc_clk); return -EIO; } - regmap_field_write(hdmi->field_ddc_sck_en, 1); - regmap_field_write(hdmi->field_ddc_sda_en, 1); + regmap_field_write(drv->field_ddc_sck_en, 1); + regmap_field_write(drv->field_ddc_sda_en, 1); for (i = 0; i < num; i++) { - err = xfer_msg(hdmi, &msgs[i]); + err = xfer_msg(drv, &msgs[i]); if (err) { ret = err; break; } } - clk_disable_unprepare(hdmi->ddc_clk); + clk_disable_unprepare(drv->ddc_clk); return ret; } @@ -199,123 +305,141 @@ static const struct i2c_algorithm sun4i_hdmi_i2c_algorithm = { .functionality = sun4i_hdmi_i2c_func, }; -static int sun4i_hdmi_init_regmap_fields(struct sun4i_hdmi *hdmi) +static int sun4i_hdmi_i2c_init_regmap_fields(struct sun4i_hdmi_i2c_drv *drv) { - hdmi->field_ddc_en = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_en); - if (IS_ERR(hdmi->field_ddc_en)) - return PTR_ERR(hdmi->field_ddc_en); - - hdmi->field_ddc_start = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_start); - if (IS_ERR(hdmi->field_ddc_start)) - return PTR_ERR(hdmi->field_ddc_start); - - hdmi->field_ddc_reset = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_reset); - if (IS_ERR(hdmi->field_ddc_reset)) - return PTR_ERR(hdmi->field_ddc_reset); - - hdmi->field_ddc_addr_reg = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_addr_reg); - if (IS_ERR(hdmi->field_ddc_addr_reg)) - return PTR_ERR(hdmi->field_ddc_addr_reg); - - hdmi->field_ddc_slave_addr = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_slave_addr); - if (IS_ERR(hdmi->field_ddc_slave_addr)) - return PTR_ERR(hdmi->field_ddc_slave_addr); - - hdmi->field_ddc_int_mask = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_int_mask); - if (IS_ERR(hdmi->field_ddc_int_mask)) - return PTR_ERR(hdmi->field_ddc_int_mask); - - hdmi->field_ddc_int_status = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_int_status); - if (IS_ERR(hdmi->field_ddc_int_status)) - return PTR_ERR(hdmi->field_ddc_int_status); - - hdmi->field_ddc_fifo_clear = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_fifo_clear); - if (IS_ERR(hdmi->field_ddc_fifo_clear)) - return PTR_ERR(hdmi->field_ddc_fifo_clear); - - hdmi->field_ddc_fifo_rx_thres = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_fifo_rx_thres); - if (IS_ERR(hdmi->field_ddc_fifo_rx_thres)) - return PTR_ERR(hdmi->field_ddc_fifo_rx_thres); - - hdmi->field_ddc_fifo_tx_thres = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_fifo_tx_thres); - if (IS_ERR(hdmi->field_ddc_fifo_tx_thres)) - return PTR_ERR(hdmi->field_ddc_fifo_tx_thres); - - hdmi->field_ddc_byte_count = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_byte_count); - if (IS_ERR(hdmi->field_ddc_byte_count)) - return PTR_ERR(hdmi->field_ddc_byte_count); - - hdmi->field_ddc_cmd = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_cmd); - if (IS_ERR(hdmi->field_ddc_cmd)) - return PTR_ERR(hdmi->field_ddc_cmd); - - hdmi->field_ddc_sda_en = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_sda_en); - if (IS_ERR(hdmi->field_ddc_sda_en)) - return PTR_ERR(hdmi->field_ddc_sda_en); - - hdmi->field_ddc_sck_en = - devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->field_ddc_sck_en); - if (IS_ERR(hdmi->field_ddc_sck_en)) - return PTR_ERR(hdmi->field_ddc_sck_en); + drv->field_ddc_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_en); + if (IS_ERR(drv->field_ddc_en)) + return PTR_ERR(drv->field_ddc_en); + + drv->field_ddc_start = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_start); + if (IS_ERR(drv->field_ddc_start)) + return PTR_ERR(drv->field_ddc_start); + + drv->field_ddc_reset = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_reset); + if (IS_ERR(drv->field_ddc_reset)) + return PTR_ERR(drv->field_ddc_reset); + + drv->field_ddc_addr_reg = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_addr_reg); + if (IS_ERR(drv->field_ddc_addr_reg)) + return PTR_ERR(drv->field_ddc_addr_reg); + + drv->field_ddc_slave_addr = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_slave_addr); + if (IS_ERR(drv->field_ddc_slave_addr)) + return PTR_ERR(drv->field_ddc_slave_addr); + + drv->field_ddc_int_mask = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_int_mask); + if (IS_ERR(drv->field_ddc_int_mask)) + return PTR_ERR(drv->field_ddc_int_mask); + + drv->field_ddc_int_status = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_int_status); + if (IS_ERR(drv->field_ddc_int_status)) + return PTR_ERR(drv->field_ddc_int_status); + + drv->field_ddc_fifo_clear = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_clear); + if (IS_ERR(drv->field_ddc_fifo_clear)) + return PTR_ERR(drv->field_ddc_fifo_clear); + + drv->field_ddc_fifo_rx_thres = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_rx_thres); + if (IS_ERR(drv->field_ddc_fifo_rx_thres)) + return PTR_ERR(drv->field_ddc_fifo_rx_thres); + + drv->field_ddc_fifo_tx_thres = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_tx_thres); + if (IS_ERR(drv->field_ddc_fifo_tx_thres)) + return PTR_ERR(drv->field_ddc_fifo_tx_thres); + + drv->field_ddc_byte_count = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_byte_count); + if (IS_ERR(drv->field_ddc_byte_count)) + return PTR_ERR(drv->field_ddc_byte_count); + + drv->field_ddc_cmd = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_cmd); + if (IS_ERR(drv->field_ddc_cmd)) + return PTR_ERR(drv->field_ddc_cmd); + + drv->field_ddc_sda_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sda_en); + if (IS_ERR(drv->field_ddc_sda_en)) + return PTR_ERR(drv->field_ddc_sda_en); + + drv->field_ddc_sck_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sck_en); + if (IS_ERR(drv->field_ddc_sck_en)) + return PTR_ERR(drv->field_ddc_sck_en); return 0; } int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi) { - struct i2c_adapter *adap; + struct sun4i_hdmi_i2c_drv *drv; int ret = 0; ret = sun4i_ddc_create(hdmi, hdmi->ddc_parent_clk); if (ret) return ret; - ret = sun4i_hdmi_init_regmap_fields(hdmi); + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + drv->dev = dev; + + drv->base = hdmi->base; + drv->regmap = hdmi->regmap; + + /* Hacks until we set up the clks etc properly from here. */ + drv->ddc_clk = hdmi->ddc_clk; + + if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a10s-hdmi")) + drv->variant = &sun5i_variant; + if (of_device_is_compatible(dev->of_node, "allwinner,sun6i-a31-hdmi")) + drv->variant = &sun6i_variant; + if (!drv->variant) { + dev_err(dev, "unsupported platform variant"); + return -EINVAL; + } + + ret = sun4i_hdmi_i2c_init_regmap_fields(drv); if (ret) return ret; - adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL); - if (!adap) - return -ENOMEM; - adap->owner = THIS_MODULE; - adap->class = I2C_CLASS_DDC; - adap->algo = &sun4i_hdmi_i2c_algorithm; - strlcpy(adap->name, "sun4i_hdmi_i2c adapter", sizeof(adap->name)); - i2c_set_adapdata(adap, hdmi); + i2c_set_adapdata(&drv->adap, drv); + drv->adap.owner = THIS_MODULE; + drv->adap.class = I2C_CLASS_DDC; + drv->adap.algo = &sun4i_hdmi_i2c_algorithm; + strlcpy(drv->adap.name, "sun4i_hdmi_i2c adapter", sizeof(drv->adap.name)); - ret = i2c_add_adapter(adap); + ret = i2c_add_adapter(&drv->adap); if (ret) return ret; - hdmi->i2c = adap; + hdmi->i2c = &drv->adap; return ret; } diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h new file mode 100644 index 00000000000000..7520330a2523df --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2016 Maxime Ripard + * Copyright (C) 2017 Chen-Yu Tsai + * Copyirght (C) 2017 Jonathan Liu + * Copyright (C) 2017 Olliver Schinagl + * + * Chen-Yu Tsai + * Maxime Ripard + * Jonathan Liu + * Olliver Schinagl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef _SUN4I_HDMI_I2C_H_ +#define _SUN4I_HDMI_I2C_H_ + +#define SUN4I_HDMI_DDC_CTRL_REG 0x500 +#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31) +#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE (1 << 8) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8) +#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0) + +#define SUN4I_HDMI_DDC_ADDR_REG 0x504 +#define SUN4I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) +#define SUN4I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) +#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) +#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & 0xff) + +#define SUN4I_HDMI_DDC_INT_STATUS_REG 0x50c +#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION BIT(7) +#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW BIT(6) +#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW BIT(5) +#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST BIT(4) +#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR BIT(3) +#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR BIT(2) +#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR BIT(1) +#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE BIT(0) + +#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \ + SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \ + SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \ + SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \ + SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \ + SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \ + SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \ +) + +#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x510 +#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n) (((n) & 0xf) << 4) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX (BIT(4) - 1) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n) ((n) & 0xf) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX (BIT(4) - 1) + +#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x518 + +#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x51c +#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX (BIT(10) - 1) + +#define SUN4I_HDMI_DDC_CMD_REG 0x520 +#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 6 +#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ 5 +#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE 3 + +#define SUN4I_HDMI_DDC_CLK_REG 0x528 +#define SUN4I_HDMI_DDC_CLK_M(m) (((m) & 0x7) << 3) +#define SUN4I_HDMI_DDC_CLK_N(n) ((n) & 0x7) + +#define SUN4I_HDMI_DDC_LINE_CTRL_REG 0x540 +#define SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE BIT(9) +#define SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE BIT(8) + +#define SUN4I_HDMI_DDC_FIFO_SIZE 16 + +/* A31 specific */ +#define SUN6I_HDMI_DDC_CTRL_REG 0x500 +#define SUN6I_HDMI_DDC_CTRL_RESET BIT(31) +#define SUN6I_HDMI_DDC_CTRL_START_CMD BIT(27) +#define SUN6I_HDMI_DDC_CTRL_SDA_ENABLE BIT(6) +#define SUN6I_HDMI_DDC_CTRL_SCL_ENABLE BIT(4) +#define SUN6I_HDMI_DDC_CTRL_ENABLE BIT(0) + +#define SUN6I_HDMI_DDC_CMD_REG 0x508 +#define SUN6I_HDMI_DDC_CMD_BYTE_COUNT(count) ((count) << 16) +/* command types in lower 3 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_ADDR_REG 0x50c +#define SUN6I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) +#define SUN6I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) +#define SUN6I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) +#define SUN6I_HDMI_DDC_ADDR_SLAVE(addr) (((addr) & 0xff) << 1) + +#define SUN6I_HDMI_DDC_INT_STATUS_REG 0x514 +#define SUN6I_HDMI_DDC_INT_STATUS_TIMEOUT BIT(8) +/* lower 8 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_FIFO_CTRL_REG 0x518 +#define SUN6I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(15) +/* lower 9 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_CLK_REG 0x520 +/* DDC CLK bit fields are the same, but the formula is not */ + +#define SUN6I_HDMI_DDC_FIFO_DATA_REG 0x580 + +#endif From 0200fbee7477defe995bc9468cb5f21119beecf7 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 10 Oct 2017 16:12:34 +0200 Subject: [PATCH 51/57] drm: sun4i: fix indenting No codechanges where done with this change, just some whitespace cleanup. Signed-off-by: Olliver Schinagl --- drivers/gpu/drm/sun4i/Kconfig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig index 882d85db90539a..c378fa62d20af0 100644 --- a/drivers/gpu/drm/sun4i/Kconfig +++ b/drivers/gpu/drm/sun4i/Kconfig @@ -16,17 +16,17 @@ config DRM_SUN4I if DRM_SUN4I config DRM_SUN4I_HDMI - tristate "Allwinner A10 HDMI Controller Support" - default DRM_SUN4I - help + tristate "Allwinner A10 HDMI Controller Support" + default DRM_SUN4I + help Choose this option if you have an Allwinner SoC with an HDMI controller. config DRM_SUN4I_HDMI_CEC - bool "Allwinner A10 HDMI CEC Support" - depends on DRM_SUN4I_HDMI - select CEC_CORE - select CEC_PIN + bool "Allwinner A10 HDMI CEC Support" + depends on DRM_SUN4I_HDMI + select CEC_CORE + select CEC_PIN help Choose this option if you have an Allwinner SoC with an HDMI controller and want to use CEC. From 80e91706b631b7cf580d5ce9afb5fe9b3eea0789 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Fri, 20 Oct 2017 15:54:38 +0200 Subject: [PATCH 52/57] wip hdmi-i2c, TODO: legacy clocks not working Signed-off-by: Olliver Schinagl --- .../boot/dts/sun7i-a20-olinuxino-lime2.dts | 6 + arch/arm/boot/dts/sun7i-a20.dtsi | 19 +- drivers/gpu/drm/sun4i/Kconfig | 10 + drivers/gpu/drm/sun4i/Makefile | 8 +- drivers/gpu/drm/sun4i/sun4i_hdmi.h | 13 +- drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c | 50 +- drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.h | 23 + drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c | 326 ++++++----- drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c | 441 +++------------ drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h | 114 ---- drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.c | 514 ++++++++++++++++++ drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.h | 197 +++++++ drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 20 +- 13 files changed, 1088 insertions(+), 653 deletions(-) create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.h delete mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.c create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.h diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts index 18230822192afe..dd25223359ca9f 100644 --- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts +++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts @@ -121,6 +121,12 @@ }; &hdmi { + ddc-i2c-bus = <&hdmi_i2c>; + status = "okay"; +}; + +&hdmi_i2c { + clock-frequency = <400000>; status = "okay"; }; diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi index 228c368537a081..21c118b133153e 100644 --- a/arch/arm/boot/dts/sun7i-a20.dtsi +++ b/arch/arm/boot/dts/sun7i-a20.dtsi @@ -577,16 +577,16 @@ hdmi: hdmi@1c16000 { compatible = "allwinner,sun7i-a20-hdmi", "allwinner,sun5i-a10s-hdmi"; - reg = <0x01c16000 0x1000>; + reg = <0x01c16000 0x500>; interrupts = ; clocks = <&ccu CLK_AHB_HDMI0>, <&ccu CLK_HDMI>, <&ccu 9>, <&ccu 18>; clock-names = "ahb", "mod", "pll-0", "pll-1"; - dmas = <&dma SUN4I_DMA_NORMAL 16>, - <&dma SUN4I_DMA_NORMAL 16>, - <&dma SUN4I_DMA_DEDICATED 24>; - dma-names = "ddc-tx", "ddc-rx", "audio-tx"; + #clock-cells = <0>; + clock-output-names = "hdmi-tmds"; + dmas = <&dma SUN4I_DMA_DEDICATED 24>; + dma-names = "audio-tx"; status = "disabled"; ports { @@ -617,6 +617,15 @@ }; }; + hdmi_i2c: i2c@016500 { + compatible = "allwinner,sun7i-a20-hdmi-i2c"; + reg = <0x01c16500 0x40>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&hdmi>; + status = "disabled"; + }; + spi2: spi@1c17000 { compatible = "allwinner,sun4i-a10-spi"; reg = <0x01c17000 0x1000>; diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig index c378fa62d20af0..c879b11e55cd0b 100644 --- a/drivers/gpu/drm/sun4i/Kconfig +++ b/drivers/gpu/drm/sun4i/Kconfig @@ -22,6 +22,16 @@ config DRM_SUN4I_HDMI Choose this option if you have an Allwinner SoC with an HDMI controller. +config I2C_SUN4I_HDMI + tristate "Allwinner A10 HDMI I2C Support" + depends on DRM_SUN4I_HDMI + default DRM_SUN4I_HDMI + select I2C + help + Choose this option if you have an Allwinner SoC with an I2C + enabled HDMI controller and want to enable the I2C channel. + + config DRM_SUN4I_HDMI_CEC bool "Allwinner A10 HDMI CEC Support" depends on DRM_SUN4I_HDMI diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile index 301b5b1452db22..f719255c7b9ad5 100644 --- a/drivers/gpu/drm/sun4i/Makefile +++ b/drivers/gpu/drm/sun4i/Makefile @@ -3,11 +3,13 @@ sun4i-backend-y += sun4i_backend.o sun4i_layer.o sun4i-drm-y += sun4i_drv.o sun4i-drm-y += sun4i_framebuffer.o -sun4i-drm-hdmi-y += sun4i_hdmi_ddc_clk.o +sun4i-drm-hdmi-y += sun4i_hdmi_i2c_drv.o sun4i_hdmi_ddc_clk.o sun4i-drm-hdmi-y += sun4i_hdmi_enc.o -sun4i-drm-hdmi-y += sun4i_hdmi_i2c.o sun4i-drm-hdmi-y += sun4i_hdmi_tmds_clk.o +sun4i-hdmi-i2c-y += sun4i_hdmi_i2c_drv.o sun4i_hdmi_ddc_clk.o +sun4i-hdmi-i2c-y += sun4i_hdmi_i2c.o + sun8i-mixer-y += sun8i_mixer.o sun8i_layer.o sun4i-tcon-y += sun4i_crtc.o @@ -23,3 +25,5 @@ obj-$(CONFIG_DRM_SUN4I) += sun6i_drc.o obj-$(CONFIG_DRM_SUN4I_BACKEND) += sun4i-backend.o obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o + +obj-$(CONFIG_I2C_SUN4I_HDMI) += sun4i-hdmi-i2c.o diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi.h b/drivers/gpu/drm/sun4i/sun4i_hdmi.h index d556a0b6fe0e73..dec5224af1ffa8 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi.h +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi.h @@ -114,17 +114,12 @@ enum sun4i_hdmi_pkt_type { }; struct sun4i_hdmi_variant { - bool has_ddc_parent_clk; bool has_reset_control; u32 pad_ctrl0_init_val; u32 pad_ctrl1_init_val; u32 pll_ctrl_init_val; - struct reg_field ddc_clk_reg; - u8 ddc_clk_pre_divider; - u8 ddc_clk_m_offset; - u8 tmds_clk_div_offset; }; @@ -134,7 +129,6 @@ struct sun4i_hdmi { struct device *dev; void __iomem *base; - struct regmap *regmap; /* Reset control */ struct reset_control *reset; @@ -142,15 +136,16 @@ struct sun4i_hdmi { /* Parent clocks */ struct clk *bus_clk; struct clk *mod_clk; - struct clk *ddc_parent_clk; struct clk *pll0_clk; struct clk *pll1_clk; /* And the clocks we create */ - struct clk *ddc_clk; struct clk *tmds_clk; + const char *tmds_clk_name; struct i2c_adapter *i2c; + /* Legacy dt i2c device */ + struct sun4i_hdmi_i2c_drv *i2c_drv; struct sun4i_drv *drv; @@ -160,8 +155,6 @@ struct sun4i_hdmi { const struct sun4i_hdmi_variant *variant; }; -int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *clk); int sun4i_tmds_create(struct sun4i_hdmi *hdmi); -int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi); #endif /* _SUN4I_HDMI_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c index 8cd6ec142fe48e..981bf6d40f0ee7 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c @@ -10,16 +10,22 @@ * the License, or (at your option) any later version. */ +#include #include +#include +#include +#include #include +#include -#include "sun4i_hdmi.h" -#include "sun4i_hdmi_i2c.h" +#include "sun4i_hdmi_i2c_drv.h" + +#define SUN4I_HDMI_I2C_CLK_NUM_PARENTS 1 struct sun4i_ddc { struct clk_hw hw; - struct sun4i_hdmi *hdmi; struct regmap_field *reg; + struct clk *parent_clk; u8 pre_div; u8 m_offset; }; @@ -110,38 +116,34 @@ static const struct clk_ops sun4i_ddc_ops = { .set_rate = sun4i_ddc_set_rate, }; -int sun4i_ddc_create(struct sun4i_hdmi *hdmi, struct clk *parent) +struct clk *sun4i_ddc_create(struct device *dev, struct regmap *regmap, + const struct sun4i_hdmi_i2c_variant *variant, + const struct clk *parent_clk) { struct clk_init_data init; struct sun4i_ddc *ddc; - const char *parent_name; + const char *parents[SUN4I_HDMI_I2C_CLK_NUM_PARENTS]; - parent_name = __clk_get_name(parent); - if (!parent_name) - return -ENODEV; + parents[0] = __clk_get_name(parent_clk); + if (!parents[0]) + return ERR_PTR(-ENODEV); - ddc = devm_kzalloc(hdmi->dev, sizeof(*ddc), GFP_KERNEL); + ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL); if (!ddc) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - ddc->reg = devm_regmap_field_alloc(hdmi->dev, hdmi->regmap, - hdmi->variant->ddc_clk_reg); + ddc->reg = devm_regmap_field_alloc(dev, regmap, variant->ddc_clk_reg); if (IS_ERR(ddc->reg)) - return PTR_ERR(ddc->reg); + return ERR_CAST(ddc->reg); - init.name = "hdmi-ddc"; + init.name = "hdmi-i2c"; init.ops = &sun4i_ddc_ops; - init.parent_names = &parent_name; - init.num_parents = 1; + init.parent_names = parents; + init.num_parents = SUN4I_HDMI_I2C_CLK_NUM_PARENTS; - ddc->hdmi = hdmi; ddc->hw.init = &init; - ddc->pre_div = hdmi->variant->ddc_clk_pre_divider; - ddc->m_offset = hdmi->variant->ddc_clk_m_offset; - - hdmi->ddc_clk = devm_clk_register(hdmi->dev, &ddc->hw); - if (IS_ERR(hdmi->ddc_clk)) - return PTR_ERR(hdmi->ddc_clk); + ddc->pre_div = variant->ddc_clk_pre_divider; + ddc->m_offset = variant->ddc_clk_m_offset; - return 0; + return devm_clk_register(dev, &ddc->hw); } diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.h b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.h new file mode 100644 index 00000000000000..b0c164393eaf4c --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2018 Olliver Schinagl + * + * Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef _SUN4I_HDMI_DDC_CLK_H_ +#define _SUN4I_HDMI_DDC_CLK_H_ + +#include +#include +#include + +#include "sun4i_hdmi_i2c_drv.h" + +struct clk *sun4i_ddc_create(struct device *dev, struct regmap *regmap, + const struct sun4i_hdmi_i2c_variant *variant, + const struct clk *parent_clk); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index 4242e4a520b1c1..253205602e1859 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -18,19 +18,23 @@ #include #include +#include #include +#include +#include #include #include +#include #include +#include #include -#include #include #include "sun4i_backend.h" #include "sun4i_crtc.h" #include "sun4i_drv.h" #include "sun4i_hdmi.h" -#include "sun4i_hdmi_i2c.h" +#include "sun4i_hdmi_i2c_drv.h" #include "sun4i_tcon.h" static inline struct sun4i_hdmi * @@ -305,27 +309,6 @@ static const struct sun4i_hdmi_variant sun4i_variant = { SUN4I_HDMI_PLL_CTRL_HV_IS_33 | SUN4I_HDMI_PLL_CTRL_BWS | SUN4I_HDMI_PLL_CTRL_PLL_EN, - - .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), - .ddc_clk_pre_divider = 2, - .ddc_clk_m_offset = 1, - - .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), - .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), - .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), - .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), - .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), - .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), - .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), - .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), - .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), - .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), - .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), - .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), - .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), - - .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, - .ddc_fifo_has_dir = true, }; static const struct sun4i_hdmi_variant sun5i_variant = { @@ -359,7 +342,6 @@ static const struct sun4i_hdmi_variant sun5i_variant = { }; static const struct sun4i_hdmi_variant sun6i_variant = { - .has_ddc_parent_clk = true, .has_reset_control = true, .pad_ctrl0_init_val = 0xff | SUN4I_HDMI_PAD_CTRL0_TXEN | @@ -394,109 +376,27 @@ static const struct sun4i_hdmi_variant sun6i_variant = { .tmds_clk_div_offset = 1, }; -static const struct regmap_config sun4i_hdmi_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .max_register = 0x580, -}; - static int sun4i_hdmi_bind(struct device *dev, struct device *master, void *data) { - struct platform_device *pdev = to_platform_device(dev); + struct device_node *i2c_np; struct drm_device *drm = data; struct sun4i_drv *drv = drm->dev_private; struct sun4i_hdmi *hdmi; - struct resource *res; u32 reg; int ret; - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; - dev_set_drvdata(dev, hdmi); - hdmi->dev = dev; - hdmi->drv = drv; - - hdmi->variant = of_device_get_match_data(dev); - if (!hdmi->variant) - return -EINVAL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdmi->base = devm_ioremap_resource(dev, res); - if (IS_ERR(hdmi->base)) { - dev_err(dev, "Couldn't map the HDMI encoder registers\n"); - return PTR_ERR(hdmi->base); + hdmi = dev_get_drvdata(dev); + if (!hdmi) { + pr_err("hdmi tmds clk not initialized\n"); + return -ENODEV; } - if (hdmi->variant->has_reset_control) { - hdmi->reset = devm_reset_control_get(dev, NULL); - if (IS_ERR(hdmi->reset)) { - dev_err(dev, "Couldn't get the HDMI reset control\n"); - return PTR_ERR(hdmi->reset); - } - - ret = reset_control_deassert(hdmi->reset); - if (ret) { - dev_err(dev, "Couldn't deassert HDMI reset\n"); - return ret; - } - } + hdmi->drv = drv; - hdmi->bus_clk = devm_clk_get(dev, "ahb"); - if (IS_ERR(hdmi->bus_clk)) { - dev_err(dev, "Couldn't get the HDMI bus clock\n"); - ret = PTR_ERR(hdmi->bus_clk); - goto err_assert_reset; - } clk_prepare_enable(hdmi->bus_clk); - - hdmi->mod_clk = devm_clk_get(dev, "mod"); - if (IS_ERR(hdmi->mod_clk)) { - dev_err(dev, "Couldn't get the HDMI mod clock\n"); - ret = PTR_ERR(hdmi->mod_clk); - goto err_disable_bus_clk; - } clk_prepare_enable(hdmi->mod_clk); - hdmi->pll0_clk = devm_clk_get(dev, "pll-0"); - if (IS_ERR(hdmi->pll0_clk)) { - dev_err(dev, "Couldn't get the HDMI PLL 0 clock\n"); - ret = PTR_ERR(hdmi->pll0_clk); - goto err_disable_mod_clk; - } - - hdmi->pll1_clk = devm_clk_get(dev, "pll-1"); - if (IS_ERR(hdmi->pll1_clk)) { - dev_err(dev, "Couldn't get the HDMI PLL 1 clock\n"); - ret = PTR_ERR(hdmi->pll1_clk); - goto err_disable_mod_clk; - } - - hdmi->regmap = devm_regmap_init_mmio(dev, hdmi->base, - &sun4i_hdmi_regmap_config); - if (IS_ERR(hdmi->regmap)) { - dev_err(dev, "Couldn't create HDMI encoder regmap\n"); - return PTR_ERR(hdmi->regmap); - } - - ret = sun4i_tmds_create(hdmi); - if (ret) { - dev_err(dev, "Couldn't create the TMDS clock\n"); - goto err_disable_mod_clk; - } - - if (hdmi->variant->has_ddc_parent_clk) { - hdmi->ddc_parent_clk = devm_clk_get(dev, "ddc"); - if (IS_ERR(hdmi->ddc_parent_clk)) { - dev_err(dev, "Couldn't get the HDMI DDC clock\n"); - return PTR_ERR(hdmi->ddc_parent_clk); - } - } else { - hdmi->ddc_parent_clk = hdmi->tmds_clk; - } - writel(SUN4I_HDMI_CTRL_ENABLE, hdmi->base + SUN4I_HDMI_CTRL_REG); writel(hdmi->variant->pad_ctrl0_init_val, @@ -507,10 +407,27 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, reg |= hdmi->variant->pll_ctrl_init_val; writel(reg, hdmi->base + SUN4I_HDMI_PLL_CTRL_REG); - ret = sun4i_hdmi_i2c_create(dev, hdmi); - if (ret) { + i2c_np = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0); + if (!i2c_np) { + dev_warn(dev, "Missing ddc-i2c-bus node\n"); + + /* legacy devicetree's do not have the hdmi-i2c node */ + hdmi->i2c_drv = sun4i_hdmi_i2c_setup(dev, hdmi->base); + if (IS_ERR(hdmi->i2c_drv)) { + if (PTR_ERR(hdmi->i2c_drv) != -EPROBE_DEFER) + dev_err(dev, "Couldn't setup HDMI I2C driver\n"); + ret = PTR_ERR(hdmi->i2c_drv); + goto err_disable_clks; + } + + hdmi->i2c = &hdmi->i2c_drv->adap; + } else { + hdmi->i2c = of_find_i2c_adapter_by_node(i2c_np); + } + if (!hdmi->i2c) { dev_err(dev, "Couldn't create the HDMI I2C adapter\n"); - goto err_disable_mod_clk; + ret = -ENODEV; + goto err_i2c_adap; } drm_encoder_helper_add(&hdmi->encoder, @@ -522,14 +439,15 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, NULL); if (ret) { dev_err(dev, "Couldn't initialise the HDMI encoder\n"); - goto err_del_i2c_adapter; + ret = -ENODEV; + goto err_i2c_adap; } hdmi->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); if (!hdmi->encoder.possible_crtcs) { ret = -EPROBE_DEFER; - goto err_del_i2c_adapter; + goto err_i2c_adap; } #ifdef CONFIG_DRM_SUN4I_HDMI_CEC @@ -568,14 +486,12 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, err_cleanup_connector: cec_delete_adapter(hdmi->cec_adap); drm_encoder_cleanup(&hdmi->encoder); -err_del_i2c_adapter: - i2c_del_adapter(hdmi->i2c); -err_disable_mod_clk: +err_i2c_adap: + if (!hdmi->i2c_drv) + put_device(&hdmi->i2c->dev); +err_disable_clks: clk_disable_unprepare(hdmi->mod_clk); -err_disable_bus_clk: clk_disable_unprepare(hdmi->bus_clk); -err_assert_reset: - reset_control_assert(hdmi->reset); return ret; } @@ -584,10 +500,12 @@ static void sun4i_hdmi_unbind(struct device *dev, struct device *master, { struct sun4i_hdmi *hdmi = dev_get_drvdata(dev); + if (!hdmi->i2c_drv) + put_device(&hdmi->i2c->dev); + sun4i_hdmi_i2c_fini(hdmi->i2c_drv); cec_unregister_adapter(hdmi->cec_adap); drm_connector_cleanup(&hdmi->connector); drm_encoder_cleanup(&hdmi->encoder); - i2c_del_adapter(hdmi->i2c); clk_disable_unprepare(hdmi->mod_clk); clk_disable_unprepare(hdmi->bus_clk); } @@ -597,14 +515,173 @@ static const struct component_ops sun4i_hdmi_ops = { .unbind = sun4i_hdmi_unbind, }; +struct clk_core { + const char *name; + const struct clk_ops *ops; + struct clk_hw *hw; + struct module *owner; + struct device *dev; + struct clk_core *parent; + const char **parent_names; + struct clk_core **parents; + u8 num_parents; + u8 new_parent_index; + unsigned long rate; + unsigned long req_rate; + unsigned long new_rate; + struct clk_core *new_parent; + struct clk_core *new_child; + unsigned long flags; + bool orphan; + unsigned int enable_count; + unsigned int prepare_count; + unsigned long min_rate; + unsigned long max_rate; + unsigned long accuracy; + int phase; + struct hlist_head children; + struct hlist_node child_node; + struct hlist_head clks; + unsigned int notifier_count; +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; + struct hlist_node debug_node; +#endif + struct kref ref; +}; +struct clk { + struct clk_core *core; + const char *dev_id; + const char *con_id; + unsigned long min_rate; + unsigned long max_rate; + struct hlist_node clks_node; +}; + +static int sun4i_hdmi_tmds_clk_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev_of_node(dev); + struct resource *res; + struct sun4i_hdmi *hdmi; + int ret; + + hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + hdmi->dev = dev; + + hdmi->variant = of_device_get_match_data(dev); + if (!hdmi->variant) { + dev_err(dev, "hdmi_tmds_clk: couldn't find matching device\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hdmi->base = devm_ioremap_resource(dev, res); + if (IS_ERR(hdmi->base)) { + dev_err(dev, "couldn't map the HDMI encoder registers\n"); + return PTR_ERR(hdmi->base); + } + + if (hdmi->variant->has_reset_control) { + hdmi->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(hdmi->reset)) { + dev_err(dev, "couldn't get the HDMI reset control\n"); + return PTR_ERR(hdmi->reset); + } + + ret = reset_control_deassert(hdmi->reset); + if (ret) { + dev_err(dev, "couldn't deassert HDMI reset\n"); + goto err_assert_reset; + } + } + + hdmi->bus_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(hdmi->bus_clk)) { + dev_err(dev, "couldn't get the HDMI bus clock\n"); + ret = PTR_ERR(hdmi->bus_clk); + goto err_assert_reset; + } + clk_prepare_enable(hdmi->bus_clk); + + hdmi->mod_clk = of_clk_get_by_name(node, "mod"); + if (IS_ERR(hdmi->mod_clk)) { + dev_err(dev, "couldn't get the HDMI mod clock\n"); + ret = PTR_ERR(hdmi->mod_clk); + goto err_disable_bus_clk; + } + clk_prepare_enable(hdmi->mod_clk); + + hdmi->pll0_clk = devm_clk_get(dev, "pll-0"); + if (IS_ERR(hdmi->pll0_clk)) { + pr_err("couldn't get the HDMI PLL 0 clock\n"); + ret = PTR_ERR(hdmi->pll0_clk); + goto err_disable_mod_clk; + } + + hdmi->pll1_clk = of_clk_get_by_name(node, "pll-1"); + if (IS_ERR(hdmi->pll1_clk)) { + dev_err(dev, "couldn't get the HDMI PLL 1 clock\n"); + ret = PTR_ERR(hdmi->pll1_clk); + goto err_disable_mod_clk; + } + + ret = of_property_read_string(node, "clock-output-names", + &hdmi->tmds_clk_name); + if (ret) { + /* Deal with old/incomplete DTs */ + hdmi->tmds_clk_name = "hdmi-tmds"; + dev_warn(dev, "no 'clock-output-names', falling back to: %s\n", + hdmi->tmds_clk_name); + } + + ret = sun4i_tmds_create(hdmi); + if (ret) { + dev_err(dev, "couldn't create the TMDS clock\n"); + goto err_disable_mod_clk; + } + ret = of_clk_add_provider(node, of_clk_src_simple_get, + hdmi->tmds_clk); + // TODO devm_of_clk_add_provider() + if (ret) { + dev_err(dev, "couldn't register the TMDS clock\n"); + goto err_disable_mod_clk; + } + + dev_set_drvdata(dev, hdmi); + + return ret; + +err_disable_mod_clk: + clk_disable_unprepare(hdmi->mod_clk); +err_disable_bus_clk: + clk_disable_unprepare(hdmi->bus_clk); +err_assert_reset: + reset_control_assert(hdmi->reset); + + return ret; +} + static int sun4i_hdmi_probe(struct platform_device *pdev) { + int ret; + + ret = sun4i_hdmi_tmds_clk_init(pdev); + if (ret) + return ret; + return component_add(&pdev->dev, &sun4i_hdmi_ops); } static int sun4i_hdmi_remove(struct platform_device *pdev) { + struct device_node *node = dev_of_node(&pdev->dev); + component_del(&pdev->dev, &sun4i_hdmi_ops); + of_clk_del_provider(node); return 0; } @@ -613,6 +690,7 @@ static const struct of_device_id sun4i_hdmi_of_table[] = { { .compatible = "allwinner,sun4i-a10-hdmi", .data = &sun4i_variant, }, { .compatible = "allwinner,sun5i-a10s-hdmi", .data = &sun5i_variant, }, { .compatible = "allwinner,sun6i-a31-hdmi", .data = &sun6i_variant, }, + { .compatible = "allwinner,sun7i-a20-hdmi", .data = &sun5i_variant, }, { } }; MODULE_DEVICE_TABLE(of, sun4i_hdmi_of_table); diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c index 24cd4251b72f74..62b7e240a87b73 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c @@ -1,92 +1,27 @@ /* - * Copyright (C) 2016 Maxime Ripard - * Copyright (C) 2017 Jonathan Liu + * Copyright (C) 2018 Olliver Schinagl + * + * Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. */ -#include -#include -#include - -#include "sun4i_hdmi.h" -#include "sun4i_hdmi_i2c.h" - -/* FIFO request bit is set when FIFO level is above RX_THRESHOLD during read */ -#define RX_THRESHOLD SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX - -struct sun4i_hdmi_i2c_variant { - struct reg_field ddc_clk_reg; - u8 ddc_clk_pre_divider; - u8 ddc_clk_m_offset; - - /* Register fields for I2C adapter */ - struct reg_field field_ddc_en; - struct reg_field field_ddc_start; - struct reg_field field_ddc_reset; - struct reg_field field_ddc_addr_reg; - struct reg_field field_ddc_slave_addr; - struct reg_field field_ddc_int_mask; - struct reg_field field_ddc_int_status; - struct reg_field field_ddc_fifo_clear; - struct reg_field field_ddc_fifo_rx_thres; - struct reg_field field_ddc_fifo_tx_thres; - struct reg_field field_ddc_byte_count; - struct reg_field field_ddc_cmd; - struct reg_field field_ddc_sda_en; - struct reg_field field_ddc_sck_en; - - /* DDC FIFO register offset */ - u32 ddc_fifo_reg; - - /* - * DDC FIFO threshold boundary conditions - * - * This is used to cope with the threshold boundary condition - * being slightly different on sun5i and sun6i. - * - * On sun5i the threshold is exclusive, i.e. does not include, - * the value of the threshold. ( > for RX; < for TX ) - * On sun6i the threshold is inclusive, i.e. includes, the - * value of the threshold. ( >= for RX; <= for TX ) - */ - bool ddc_fifo_thres_incl; - - bool ddc_fifo_has_dir; -}; - -struct sun4i_hdmi_i2c_drv { - struct device *dev; - - void __iomem *base; - struct regmap *regmap; - - struct clk *ddc_clk; +#include +#include +#include +#include +#include +#include +#include - struct i2c_adapter adap; +#include "sun4i_hdmi_i2c_drv.h" - struct regmap_field *field_ddc_en; - struct regmap_field *field_ddc_start; - struct regmap_field *field_ddc_reset; - struct regmap_field *field_ddc_addr_reg; - struct regmap_field *field_ddc_slave_addr; - struct regmap_field *field_ddc_int_mask; - struct regmap_field *field_ddc_int_status; - struct regmap_field *field_ddc_fifo_clear; - struct regmap_field *field_ddc_fifo_rx_thres; - struct regmap_field *field_ddc_fifo_tx_thres; - struct regmap_field *field_ddc_byte_count; - struct regmap_field *field_ddc_cmd; - struct regmap_field *field_ddc_sda_en; - struct regmap_field *field_ddc_sck_en; - - const struct sun4i_hdmi_i2c_variant *variant; -}; +#define SUN4I_HDMI_I2C_DRIVER_NAME "sun4i-hdmi-i2c" -static const struct sun4i_hdmi_i2c_variant sun5i_variant = { +static const struct sun4i_hdmi_i2c_variant sun4i_variant = { + .has_legacy_dt = true, + .ddc_parent_clk_name = NULL, .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), .ddc_clk_pre_divider = 2, .ddc_clk_m_offset = 1, @@ -110,6 +45,8 @@ static const struct sun4i_hdmi_i2c_variant sun5i_variant = { }; static const struct sun4i_hdmi_i2c_variant sun6i_variant = { + .has_legacy_dt = true, + .ddc_parent_clk_name = NULL, .ddc_clk_reg = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG, 0, 6), .ddc_clk_pre_divider = 1, .ddc_clk_m_offset = 2, @@ -132,314 +69,72 @@ static const struct sun4i_hdmi_i2c_variant sun6i_variant = { .ddc_fifo_thres_incl = true, }; -static int fifo_transfer(struct sun4i_hdmi_i2c_drv *drv, u8 *buf, int len, bool read) -{ - /* - * 1 byte takes 9 clock cycles (8 bits + 1 ACK) = 90 us for 100 kHz - * clock. As clock rate is fixed, just round it up to 100 us. - */ - const unsigned long byte_time_ns = 100; - const u32 mask = SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | - SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | - SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE; - u32 reg; - /* - * If threshold is inclusive, then the FIFO may only have - * RX_THRESHOLD number of bytes, instead of RX_THRESHOLD + 1. - */ - int read_len = RX_THRESHOLD + - (drv->variant->ddc_fifo_thres_incl ? 0 : 1); - - /* - * Limit transfer length by FIFO threshold or FIFO size. - * For TX the threshold is for an empty FIFO. - */ - len = min_t(int, len, read ? read_len : SUN4I_HDMI_DDC_FIFO_SIZE); - - /* Wait until error, FIFO request bit set or transfer complete */ - if (regmap_field_read_poll_timeout(drv->field_ddc_int_status, reg, - reg & mask, len * byte_time_ns, - 100000)) - return -ETIMEDOUT; - - if (reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) - return -EIO; - - if (read) - readsb(drv->base + drv->variant->ddc_fifo_reg, buf, len); - else - writesb(drv->base + drv->variant->ddc_fifo_reg, buf, len); - - /* Clear FIFO request bit by forcing a write to that bit */ - regmap_field_force_write(drv->field_ddc_int_status, - SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST); - - return len; -} - -static int xfer_msg(struct sun4i_hdmi_i2c_drv *drv, struct i2c_msg *msg) -{ - int i, len; - u32 reg; - - /* Set FIFO direction */ - if (drv->variant->ddc_fifo_has_dir) { - reg = readl(drv->base + SUN4I_HDMI_DDC_CTRL_REG); - reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK; - reg |= (msg->flags & I2C_M_RD) ? - SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ : - SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE; - writel(reg, drv->base + SUN4I_HDMI_DDC_CTRL_REG); - } - - /* Clear address register (not cleared by soft reset) */ - regmap_field_write(drv->field_ddc_addr_reg, 0); - - /* Set I2C address */ - regmap_field_write(drv->field_ddc_slave_addr, msg->addr); - - /* - * Set FIFO RX/TX thresholds and clear FIFO - * - * If threshold is inclusive, we can set the TX threshold to - * 0 instead of 1. - */ - regmap_field_write(drv->field_ddc_fifo_tx_thres, - drv->variant->ddc_fifo_thres_incl ? 0 : 1); - regmap_field_write(drv->field_ddc_fifo_rx_thres, RX_THRESHOLD); - regmap_field_write(drv->field_ddc_fifo_clear, 1); - if (regmap_field_read_poll_timeout(drv->field_ddc_fifo_clear, - reg, !reg, 100, 2000)) - return -EIO; - - /* Set transfer length */ - regmap_field_write(drv->field_ddc_byte_count, msg->len); - - /* Set command */ - regmap_field_write(drv->field_ddc_cmd, - msg->flags & I2C_M_RD ? - SUN4I_HDMI_DDC_CMD_IMPLICIT_READ : - SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE); - - /* Clear interrupt status bits by forcing a write */ - regmap_field_force_write(drv->field_ddc_int_status, - SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | - SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | - SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE); - - /* Start command */ - regmap_field_write(drv->field_ddc_start, 1); - - /* Transfer bytes */ - for (i = 0; i < msg->len; i += len) { - len = fifo_transfer(drv, msg->buf + i, msg->len - i, - msg->flags & I2C_M_RD); - if (len <= 0) - return len; - } - - /* Wait for command to finish */ - if (regmap_field_read_poll_timeout(drv->field_ddc_start, - reg, !reg, 100, 100000)) - return -EIO; - - /* Check for errors */ - regmap_field_read(drv->field_ddc_int_status, ®); - if ((reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) || - !(reg & SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE)) { - return -EIO; - } +static const struct regmap_config sun4i_hdmi_i2c_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x80, +}; - return 0; -} +static const struct of_device_id sun4i_hdmi_i2c_of_table[] = { + { .compatible = "allwinner,sun4i-a10-hdmi-i2c", .data = &sun4i_variant }, + { .compatible = "allwinner,sun5i-a10s-hdmi-i2c", .data = &sun4i_variant }, + { .compatible = "allwinner,sun6i-a31-hdmi-i2c", .data = &sun6i_variant }, + { .compatible = "allwinner,sun7i-a20-hdmi-i2c", .data = &sun4i_variant }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun4i_hdmi_i2c_of_table); -static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap, - struct i2c_msg *msgs, int num) +static int sun4i_hdmi_i2c_probe(struct platform_device *pdev) { - struct sun4i_hdmi_i2c_drv *drv = i2c_get_adapdata(adap); - u32 reg; - int err, i, ret = num; - - for (i = 0; i < num; i++) { - if (!msgs[i].len) - return -EINVAL; - if (msgs[i].len > SUN4I_HDMI_DDC_BYTE_COUNT_MAX) - return -EINVAL; - } + struct sun4i_hdmi_i2c_drv *drv; + struct resource *res; + void __iomem *base; - /* DDC clock needs to be enabled for the module to work */ - clk_prepare_enable(drv->ddc_clk); - clk_set_rate(drv->ddc_clk, 100000); + if (!pdev) + return -ENODEV; - /* Reset I2C controller */ - regmap_field_write(drv->field_ddc_en, 1); - regmap_field_write(drv->field_ddc_reset, 1); - if (regmap_field_read_poll_timeout(drv->field_ddc_reset, - reg, !reg, 100, 2000)) { - clk_disable_unprepare(drv->ddc_clk); - return -EIO; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "couldn't map the HDMI-I2C registers\n"); + return PTR_ERR(base); } - regmap_field_write(drv->field_ddc_sck_en, 1); - regmap_field_write(drv->field_ddc_sda_en, 1); - - for (i = 0; i < num; i++) { - err = xfer_msg(drv, &msgs[i]); - if (err) { - ret = err; - break; - } + drv = sun4i_hdmi_i2c_init(&pdev->dev, base, sun4i_hdmi_i2c_of_table, + &sun4i_hdmi_i2c_regmap_config); + if (IS_ERR(drv)) { + if (PTR_ERR(drv) != -EPROBE_DEFER) + dev_err(&pdev->dev, "couldn't setup HDMI-I2C driver\n"); + return PTR_ERR(drv); } - clk_disable_unprepare(drv->ddc_clk); - return ret; -} + platform_set_drvdata(pdev, drv); -static u32 sun4i_hdmi_i2c_func(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return 0; } -static const struct i2c_algorithm sun4i_hdmi_i2c_algorithm = { - .master_xfer = sun4i_hdmi_i2c_xfer, - .functionality = sun4i_hdmi_i2c_func, -}; -static int sun4i_hdmi_i2c_init_regmap_fields(struct sun4i_hdmi_i2c_drv *drv) +static int sun4i_hdmi_i2c_remove(struct platform_device *pdev) { - drv->field_ddc_en = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_en); - if (IS_ERR(drv->field_ddc_en)) - return PTR_ERR(drv->field_ddc_en); - - drv->field_ddc_start = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_start); - if (IS_ERR(drv->field_ddc_start)) - return PTR_ERR(drv->field_ddc_start); - - drv->field_ddc_reset = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_reset); - if (IS_ERR(drv->field_ddc_reset)) - return PTR_ERR(drv->field_ddc_reset); - - drv->field_ddc_addr_reg = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_addr_reg); - if (IS_ERR(drv->field_ddc_addr_reg)) - return PTR_ERR(drv->field_ddc_addr_reg); - - drv->field_ddc_slave_addr = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_slave_addr); - if (IS_ERR(drv->field_ddc_slave_addr)) - return PTR_ERR(drv->field_ddc_slave_addr); - - drv->field_ddc_int_mask = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_int_mask); - if (IS_ERR(drv->field_ddc_int_mask)) - return PTR_ERR(drv->field_ddc_int_mask); - - drv->field_ddc_int_status = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_int_status); - if (IS_ERR(drv->field_ddc_int_status)) - return PTR_ERR(drv->field_ddc_int_status); - - drv->field_ddc_fifo_clear = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_fifo_clear); - if (IS_ERR(drv->field_ddc_fifo_clear)) - return PTR_ERR(drv->field_ddc_fifo_clear); - - drv->field_ddc_fifo_rx_thres = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_fifo_rx_thres); - if (IS_ERR(drv->field_ddc_fifo_rx_thres)) - return PTR_ERR(drv->field_ddc_fifo_rx_thres); - - drv->field_ddc_fifo_tx_thres = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_fifo_tx_thres); - if (IS_ERR(drv->field_ddc_fifo_tx_thres)) - return PTR_ERR(drv->field_ddc_fifo_tx_thres); + struct sun4i_hdmi_i2c_drv *drv = platform_get_drvdata(pdev); - drv->field_ddc_byte_count = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_byte_count); - if (IS_ERR(drv->field_ddc_byte_count)) - return PTR_ERR(drv->field_ddc_byte_count); - - drv->field_ddc_cmd = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_cmd); - if (IS_ERR(drv->field_ddc_cmd)) - return PTR_ERR(drv->field_ddc_cmd); - - drv->field_ddc_sda_en = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_sda_en); - if (IS_ERR(drv->field_ddc_sda_en)) - return PTR_ERR(drv->field_ddc_sda_en); - - drv->field_ddc_sck_en = - devm_regmap_field_alloc(drv->dev, drv->regmap, - drv->variant->field_ddc_sck_en); - if (IS_ERR(drv->field_ddc_sck_en)) - return PTR_ERR(drv->field_ddc_sck_en); + sun4i_hdmi_i2c_fini(drv); return 0; } -int sun4i_hdmi_i2c_create(struct device *dev, struct sun4i_hdmi *hdmi) -{ - struct sun4i_hdmi_i2c_drv *drv; - int ret = 0; - - ret = sun4i_ddc_create(hdmi, hdmi->ddc_parent_clk); - if (ret) - return ret; - - drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); - if (!drv) - return -ENOMEM; - - drv->dev = dev; - - drv->base = hdmi->base; - drv->regmap = hdmi->regmap; - - /* Hacks until we set up the clks etc properly from here. */ - drv->ddc_clk = hdmi->ddc_clk; - - if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a10s-hdmi")) - drv->variant = &sun5i_variant; - if (of_device_is_compatible(dev->of_node, "allwinner,sun6i-a31-hdmi")) - drv->variant = &sun6i_variant; - if (!drv->variant) { - dev_err(dev, "unsupported platform variant"); - return -EINVAL; - } - - ret = sun4i_hdmi_i2c_init_regmap_fields(drv); - if (ret) - return ret; - - - i2c_set_adapdata(&drv->adap, drv); - drv->adap.owner = THIS_MODULE; - drv->adap.class = I2C_CLASS_DDC; - drv->adap.algo = &sun4i_hdmi_i2c_algorithm; - strlcpy(drv->adap.name, "sun4i_hdmi_i2c adapter", sizeof(drv->adap.name)); - - ret = i2c_add_adapter(&drv->adap); - if (ret) - return ret; - - hdmi->i2c = &drv->adap; +static struct platform_driver sun4i_hdmi_i2c_driver = { + .probe = sun4i_hdmi_i2c_probe, + .remove = sun4i_hdmi_i2c_remove, + .driver = { + .name = SUN4I_HDMI_I2C_DRIVER_NAME, + .of_match_table = sun4i_hdmi_i2c_of_table, + }, +}; +module_platform_driver(sun4i_hdmi_i2c_driver); - return ret; -} +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Olliver Schinagl "); +MODULE_DESCRIPTION("I2C adapter driver for Allwinner sunxi HDMI I2C bus"); +MODULE_ALIAS("platform:" SUN4I_HDMI_I2C_DRIVER_NAME); diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h deleted file mode 100644 index 7520330a2523df..00000000000000 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2016 Maxime Ripard - * Copyright (C) 2017 Chen-Yu Tsai - * Copyirght (C) 2017 Jonathan Liu - * Copyright (C) 2017 Olliver Schinagl - * - * Chen-Yu Tsai - * Maxime Ripard - * Jonathan Liu - * Olliver Schinagl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - */ - -#ifndef _SUN4I_HDMI_I2C_H_ -#define _SUN4I_HDMI_I2C_H_ - -#define SUN4I_HDMI_DDC_CTRL_REG 0x500 -#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31) -#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE (1 << 8) -#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8) -#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0) - -#define SUN4I_HDMI_DDC_ADDR_REG 0x504 -#define SUN4I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) -#define SUN4I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) -#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) -#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & 0xff) - -#define SUN4I_HDMI_DDC_INT_STATUS_REG 0x50c -#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION BIT(7) -#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW BIT(6) -#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW BIT(5) -#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST BIT(4) -#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR BIT(3) -#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR BIT(2) -#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR BIT(1) -#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE BIT(0) - -#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \ - SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \ - SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \ - SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \ - SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \ - SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \ - SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \ -) - -#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x510 -#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n) (((n) & 0xf) << 4) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4) -#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX (BIT(4) - 1) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n) ((n) & 0xf) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0) -#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX (BIT(4) - 1) - -#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x518 - -#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x51c -#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX (BIT(10) - 1) - -#define SUN4I_HDMI_DDC_CMD_REG 0x520 -#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 6 -#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ 5 -#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE 3 - -#define SUN4I_HDMI_DDC_CLK_REG 0x528 -#define SUN4I_HDMI_DDC_CLK_M(m) (((m) & 0x7) << 3) -#define SUN4I_HDMI_DDC_CLK_N(n) ((n) & 0x7) - -#define SUN4I_HDMI_DDC_LINE_CTRL_REG 0x540 -#define SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE BIT(9) -#define SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE BIT(8) - -#define SUN4I_HDMI_DDC_FIFO_SIZE 16 - -/* A31 specific */ -#define SUN6I_HDMI_DDC_CTRL_REG 0x500 -#define SUN6I_HDMI_DDC_CTRL_RESET BIT(31) -#define SUN6I_HDMI_DDC_CTRL_START_CMD BIT(27) -#define SUN6I_HDMI_DDC_CTRL_SDA_ENABLE BIT(6) -#define SUN6I_HDMI_DDC_CTRL_SCL_ENABLE BIT(4) -#define SUN6I_HDMI_DDC_CTRL_ENABLE BIT(0) - -#define SUN6I_HDMI_DDC_CMD_REG 0x508 -#define SUN6I_HDMI_DDC_CMD_BYTE_COUNT(count) ((count) << 16) -/* command types in lower 3 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_ADDR_REG 0x50c -#define SUN6I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) -#define SUN6I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) -#define SUN6I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) -#define SUN6I_HDMI_DDC_ADDR_SLAVE(addr) (((addr) & 0xff) << 1) - -#define SUN6I_HDMI_DDC_INT_STATUS_REG 0x514 -#define SUN6I_HDMI_DDC_INT_STATUS_TIMEOUT BIT(8) -/* lower 8 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_FIFO_CTRL_REG 0x518 -#define SUN6I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(15) -/* lower 9 bits are the same as sun4i */ - -#define SUN6I_HDMI_DDC_CLK_REG 0x520 -/* DDC CLK bit fields are the same, but the formula is not */ - -#define SUN6I_HDMI_DDC_FIFO_DATA_REG 0x580 - -#endif diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.c new file mode 100644 index 00000000000000..1d6c9ee5d8bb43 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.c @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2016 Maxime Ripard + * Copyright (C) 2017 Chen-Yu Tsai + * Copyright (C) 2017 Jonathan Liu + * Copyright (C) 2017 Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sun4i_hdmi_ddc_clk.h" +#include "sun4i_hdmi_i2c_drv.h" + +#define SUN4I_HDMI_I2C_SPEED_MAX 25000000 +#define SUN4I_HDMI_I2C_SPEED_DEFAULT 100000 + +/* FIFO request bit is set when FIFO level is above RX_THRESHOLD during read */ +#define RX_THRESHOLD SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX + +static const struct sun4i_hdmi_i2c_variant sun4i_legacy_variant = { + .has_legacy_dt = false, + .ddc_parent_clk_name = "hdmi-tmds", + .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 6), + .ddc_clk_pre_divider = 4, + .ddc_clk_m_offset = 1, + + .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 31, 31), + .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 30, 30), + .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 0), + .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 31), + .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 6), + .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 8), + .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 31, 31), + .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 4, 7), + .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 3), + .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 9), + .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 2), + .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 9, 9), + .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 8, 8), + + .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG + + SUN4I_HDMI_DDC_OFFSET, + .ddc_fifo_has_dir = true, +}; + +static const struct sun4i_hdmi_i2c_variant sun6i_legacy_variant = { + .has_legacy_dt = true, + .ddc_parent_clk_name = "ddc", + .ddc_clk_reg = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 6), + .ddc_clk_pre_divider = 1, + .ddc_clk_m_offset = 2, + + .field_ddc_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 0), + .field_ddc_start = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 27, 27), + .field_ddc_reset = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 31, 31), + .field_ddc_addr_reg = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG + + SUN4I_HDMI_DDC_OFFSET, 1, 31), + .field_ddc_slave_addr = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG + + SUN4I_HDMI_DDC_OFFSET, 1, 7), + .field_ddc_int_status = REG_FIELD(SUN6I_HDMI_DDC_INT_STATUS_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 8), + .field_ddc_fifo_clear = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 18, 18), + .field_ddc_fifo_rx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 4, 7), + .field_ddc_fifo_tx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 3), + .field_ddc_byte_count = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG + + SUN4I_HDMI_DDC_OFFSET, 16, 25), + .field_ddc_cmd = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG + + SUN4I_HDMI_DDC_OFFSET, 0, 2), + .field_ddc_sda_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 6, 6), + .field_ddc_sck_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG + + SUN4I_HDMI_DDC_OFFSET, 4, 4), + + .ddc_fifo_reg = SUN6I_HDMI_DDC_FIFO_DATA_REG + + SUN4I_HDMI_DDC_OFFSET, + .ddc_fifo_thres_incl = true, +}; + +static const struct regmap_config sun4i_hdmi_i2c_legacy_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x580, +}; + +static int fifo_transfer(struct sun4i_hdmi_i2c_drv *drv, u8 *buf, int len, bool read) +{ + /* + * 1 byte takes 9 clock cycles (8 bits + 1 ACK) = 90 us for 100 kHz + * clock. As clock rate is fixed, just round it up to 100 us. + */ + const unsigned long byte_time_ns = 100; + const u32 mask = SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | + SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | + SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE; + u32 reg; + /* + * If threshold is inclusive, then the FIFO may only have + * RX_THRESHOLD number of bytes, instead of RX_THRESHOLD + 1. + */ + int read_len = RX_THRESHOLD + + (drv->variant->ddc_fifo_thres_incl ? 0 : 1); + + /* + * Limit transfer length by FIFO threshold or FIFO size. + * For TX the threshold is for an empty FIFO. + */ + len = min_t(int, len, read ? read_len : SUN4I_HDMI_DDC_FIFO_SIZE); + + /* Wait until error, FIFO request bit set or transfer complete */ + if (regmap_field_read_poll_timeout(drv->field_ddc_int_status, reg, + reg & mask, len * byte_time_ns, + 100000)) + return -ETIMEDOUT; + + if (reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) + return -EIO; + + if (read) + readsb(drv->base + drv->variant->ddc_fifo_reg, buf, len); + else + writesb(drv->base + drv->variant->ddc_fifo_reg, buf, len); + + /* Clear FIFO request bit by forcing a write to that bit */ + regmap_field_force_write(drv->field_ddc_int_status, + SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST); + + return len; +} + +static int xfer_msg(struct sun4i_hdmi_i2c_drv *drv, struct i2c_msg *msg) +{ + int i, len; + u32 reg; + + /* Set FIFO direction */ + if (drv->variant->ddc_fifo_has_dir) { + reg = readl(drv->base + SUN4I_HDMI_DDC_CTRL_REG); + reg &= ~SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK; + reg |= (msg->flags & I2C_M_RD) ? + SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ : + SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE; + writel(reg, drv->base + SUN4I_HDMI_DDC_CTRL_REG); + } + + /* Clear address register (not cleared by soft reset) */ + regmap_field_write(drv->field_ddc_addr_reg, 0); + + /* Set I2C address */ + regmap_field_write(drv->field_ddc_slave_addr, msg->addr); + + /* + * Set FIFO RX/TX thresholds and clear FIFO + * + * If threshold is inclusive, we can set the TX threshold to + * 0 instead of 1. + */ + regmap_field_write(drv->field_ddc_fifo_tx_thres, + drv->variant->ddc_fifo_thres_incl ? 0 : 1); + regmap_field_write(drv->field_ddc_fifo_rx_thres, RX_THRESHOLD); + regmap_field_write(drv->field_ddc_fifo_clear, 1); + if (regmap_field_read_poll_timeout(drv->field_ddc_fifo_clear, + reg, !reg, 100, 2000)) + return -EIO; + + /* Set transfer length */ + regmap_field_write(drv->field_ddc_byte_count, msg->len); + + /* Set command */ + regmap_field_write(drv->field_ddc_cmd, + msg->flags & I2C_M_RD ? + SUN4I_HDMI_DDC_CMD_IMPLICIT_READ : + SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE); + + /* Clear interrupt status bits by forcing a write */ + regmap_field_force_write(drv->field_ddc_int_status, + SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK | + SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST | + SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE); + + /* Start command */ + regmap_field_write(drv->field_ddc_start, 1); + + /* Transfer bytes */ + for (i = 0; i < msg->len; i += len) { + len = fifo_transfer(drv, msg->buf + i, msg->len - i, + msg->flags & I2C_M_RD); + if (len <= 0) + return len; + } + + /* Wait for command to finish */ + if (regmap_field_read_poll_timeout(drv->field_ddc_start, + reg, !reg, 100, 100000)) + return -EIO; + + /* Check for errors */ + regmap_field_read(drv->field_ddc_int_status, ®); + if ((reg & SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK) || + !(reg & SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE)) { + return -EIO; + } + + return 0; +} + +static int sun4i_hdmi_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct sun4i_hdmi_i2c_drv *drv = i2c_get_adapdata(adap); + u32 reg; + int err, i, ret = num; + + for (i = 0; i < num; i++) { + if (!msgs[i].len) + return -EINVAL; + if (msgs[i].len > SUN4I_HDMI_DDC_BYTE_COUNT_MAX) + return -EINVAL; + } + + /* DDC clock needs to be enabled for the module to work */ + clk_prepare_enable(drv->ddc_clk); + + /* Reset I2C controller */ + regmap_field_write(drv->field_ddc_en, 1); + regmap_field_write(drv->field_ddc_reset, 1); + if (regmap_field_read_poll_timeout(drv->field_ddc_reset, + reg, !reg, 100, 2000)) { + clk_disable_unprepare(drv->ddc_clk); + return -EIO; + } + + regmap_field_write(drv->field_ddc_sck_en, 1); + regmap_field_write(drv->field_ddc_sda_en, 1); + + for (i = 0; i < num; i++) { + err = xfer_msg(drv, &msgs[i]); + if (err) { + ret = err; + break; + } + } + + clk_disable_unprepare(drv->ddc_clk); + return ret; +} + +static u32 sun4i_hdmi_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sun4i_hdmi_i2c_algorithm = { + .master_xfer = sun4i_hdmi_i2c_xfer, + .functionality = sun4i_hdmi_i2c_func, +}; + +static int sun4i_hdmi_i2c_init_regmap_fields(struct sun4i_hdmi_i2c_drv *drv) +{ + drv->field_ddc_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_en); + if (IS_ERR(drv->field_ddc_en)) + return PTR_ERR(drv->field_ddc_en); + + drv->field_ddc_start = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_start); + if (IS_ERR(drv->field_ddc_start)) + return PTR_ERR(drv->field_ddc_start); + + drv->field_ddc_reset = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_reset); + if (IS_ERR(drv->field_ddc_reset)) + return PTR_ERR(drv->field_ddc_reset); + + drv->field_ddc_addr_reg = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_addr_reg); + if (IS_ERR(drv->field_ddc_addr_reg)) + return PTR_ERR(drv->field_ddc_addr_reg); + + drv->field_ddc_slave_addr = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_slave_addr); + if (IS_ERR(drv->field_ddc_slave_addr)) + return PTR_ERR(drv->field_ddc_slave_addr); + + drv->field_ddc_int_mask = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_int_mask); + if (IS_ERR(drv->field_ddc_int_mask)) + return PTR_ERR(drv->field_ddc_int_mask); + + drv->field_ddc_int_status = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_int_status); + if (IS_ERR(drv->field_ddc_int_status)) + return PTR_ERR(drv->field_ddc_int_status); + + drv->field_ddc_fifo_clear = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_clear); + if (IS_ERR(drv->field_ddc_fifo_clear)) + return PTR_ERR(drv->field_ddc_fifo_clear); + + drv->field_ddc_fifo_rx_thres = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_rx_thres); + if (IS_ERR(drv->field_ddc_fifo_rx_thres)) + return PTR_ERR(drv->field_ddc_fifo_rx_thres); + + drv->field_ddc_fifo_tx_thres = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_fifo_tx_thres); + if (IS_ERR(drv->field_ddc_fifo_tx_thres)) + return PTR_ERR(drv->field_ddc_fifo_tx_thres); + + drv->field_ddc_byte_count = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_byte_count); + if (IS_ERR(drv->field_ddc_byte_count)) + return PTR_ERR(drv->field_ddc_byte_count); + + drv->field_ddc_cmd = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_cmd); + if (IS_ERR(drv->field_ddc_cmd)) + return PTR_ERR(drv->field_ddc_cmd); + + drv->field_ddc_sda_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sda_en); + if (IS_ERR(drv->field_ddc_sda_en)) + return PTR_ERR(drv->field_ddc_sda_en); + + drv->field_ddc_sck_en = + devm_regmap_field_alloc(drv->dev, drv->regmap, + drv->variant->field_ddc_sck_en); + if (IS_ERR(drv->field_ddc_sck_en)) + return PTR_ERR(drv->field_ddc_sck_en); + + return 0; +} + +struct sun4i_hdmi_i2c_drv +*sun4i_hdmi_i2c_init(struct device *dev, void __iomem *base, + const struct of_device_id *of_id_table, + const struct regmap_config *regmap_config) +{ + struct sun4i_hdmi_i2c_drv *drv; + const struct of_device_id *of_id; + struct device_node *node = dev_of_node(dev); + int ret; + + if ((!dev) || (!base) || (!regmap_config) || (!of_id_table)) + return ERR_PTR(-ENODEV); + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return ERR_PTR(-ENOMEM); + + drv->dev = dev; + drv->base = base; + + of_id = of_match_device(of_id_table, drv->dev); + if (!of_id) { + dev_err(drv->dev, "missing platform data\n"); + return ERR_PTR(-ENODEV); + } + drv->variant = of_id->data; + // TODO: of_dev_get_data + + if (!drv->variant->has_legacy_dt) { + /* TODO: get the clock previously registered */ + drv->ddc_parent_clk = of_clk_get_from_provider(NULL); + } else { + drv->ddc_parent_clk = devm_clk_get(drv->dev, + drv->variant->ddc_parent_clk_name); + } + if (IS_ERR(drv->ddc_parent_clk)) { + if (PTR_ERR(drv->ddc_parent_clk) != -EPROBE_DEFER) + dev_err(drv->dev, "couldn't get the HDMI-I2C clock\n"); + return ERR_CAST(drv->ddc_parent_clk); + } + + ret = of_property_read_u32(node, "clock-frequency", + &drv->clock_freq); + if (ret || (drv->clock_freq > SUN4I_HDMI_I2C_SPEED_MAX)) + drv->clock_freq = SUN4I_HDMI_I2C_SPEED_DEFAULT; + + drv->regmap = devm_regmap_init_mmio(drv->dev, drv->base, regmap_config); + if (IS_ERR(drv->regmap)) { + dev_err(drv->dev, "couldn't create HDMI-I2C regmap\n"); + return ERR_CAST(drv->regmap); + } + + ret = sun4i_hdmi_i2c_init_regmap_fields(drv); + if (ret) { + dev_err(drv->dev, "couldn't init HDMI-I2C regmap fields\n"); + return ERR_PTR(ret); + } + + drv->ddc_clk = sun4i_ddc_create(drv->dev, drv->regmap, drv->variant, + drv->ddc_parent_clk); + if (IS_ERR(drv->ddc_clk)) { + dev_err(drv->dev, "couldn't create the HDMI-I2C clock\n"); + return ERR_CAST(drv->ddc_clk); + } + + ret = of_clk_add_provider(node, of_clk_src_simple_get, drv->ddc_clk); + if (ret) { + dev_err(drv->dev, "couldn't register the HDMI-I2C clock\n"); + return ERR_PTR(ret); + } + + ret = clk_prepare_enable(drv->ddc_clk); + if (ret) { + dev_err(drv->dev, "unable to enable HDMI-I2C clock\n"); + return ERR_PTR(ret); + } + + ret = clk_set_rate(drv->ddc_clk, drv->clock_freq); + if (ret) { + dev_err(drv->dev, "unable to set HDMI-I2C clock rate\n"); + goto ddc_clk_err; + } + + i2c_set_adapdata(&drv->adap, drv); + drv->adap.dev.parent = drv->dev; + drv->adap.owner = THIS_MODULE; + drv->adap.class = I2C_CLASS_DDC; + drv->adap.algo = &sun4i_hdmi_i2c_algorithm; + drv->adap.dev.of_node = node; + strlcpy(drv->adap.name, "sun4i_hdmi_i2c adapter", sizeof(drv->adap.name)); + + clk_disable_unprepare(drv->ddc_clk); + + ret = i2c_add_adapter(&drv->adap); + if (ret) { + dev_err(drv->dev, "unable to create HDMI-I2C adapter\n"); + goto ddc_clk_err; + } + + return drv; + +ddc_clk_err: + clk_disable_unprepare(drv->ddc_clk); + + return ERR_PTR(ret); +} + +static const struct of_device_id sun4i_hdmi_i2c_legacy_of_table[] = { + { .compatible = "allwinner,sun4i-a10-hdmi", .data = &sun4i_legacy_variant }, + { .compatible = "allwinner,sun5i-a10s-hdmi", .data = &sun4i_legacy_variant }, + { .compatible = "allwinner,sun6i-a31-hdmi", .data = &sun6i_legacy_variant }, + { .compatible = "allwinner,sun7i-a20-hdmi", .data = &sun4i_legacy_variant }, + { /* sentinel */ } +}; + +struct sun4i_hdmi_i2c_drv *sun4i_hdmi_i2c_setup(struct device *dev, + void __iomem *base) +{ + return sun4i_hdmi_i2c_init(dev, base, sun4i_hdmi_i2c_legacy_of_table, + &sun4i_hdmi_i2c_legacy_regmap_config); +} + +void sun4i_hdmi_i2c_fini(struct sun4i_hdmi_i2c_drv *drv) +{ + struct device_node *node = dev_of_node(drv->dev); + + clk_prepare_enable(drv->ddc_clk); + i2c_del_adapter(&drv->adap); + clk_disable_unprepare(drv->ddc_clk); + of_clk_del_provider(node); +} diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.h b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.h new file mode 100644 index 00000000000000..8b296369c3f783 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c_drv.h @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2018 Olliver Schinagl + * + * Olliver Schinagl + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ + +#ifndef _SUN4I_HDMI_I2C_DRV_H_ +#define _SUN4I_HDMI_I2C_DRV_H_ + +#include +#include +#include +#include +#include +#include + +#define SUN4I_HDMI_DDC_OFFSET 0x500 + +#define SUN4I_HDMI_DDC_CTRL_REG 0x00 +#define SUN4I_HDMI_DDC_CTRL_ENABLE BIT(31) +#define SUN4I_HDMI_DDC_CTRL_START_CMD BIT(30) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_MASK BIT(8) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_WRITE (1 << 8) +#define SUN4I_HDMI_DDC_CTRL_FIFO_DIR_READ (0 << 8) +#define SUN4I_HDMI_DDC_CTRL_RESET BIT(0) + +#define SUN4I_HDMI_DDC_ADDR_REG 0x04 +#define SUN4I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) +#define SUN4I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) +#define SUN4I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) +#define SUN4I_HDMI_DDC_ADDR_SLAVE(addr) ((addr) & 0xff) + +#define SUN4I_HDMI_DDC_INT_STATUS_REG 0x0c +#define SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION BIT(7) +#define SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW BIT(6) +#define SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW BIT(5) +#define SUN4I_HDMI_DDC_INT_STATUS_FIFO_REQUEST BIT(4) +#define SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR BIT(3) +#define SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR BIT(2) +#define SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR BIT(1) +#define SUN4I_HDMI_DDC_INT_STATUS_TRANSFER_COMPLETE BIT(0) + +#define SUN4I_HDMI_DDC_INT_STATUS_ERROR_MASK ( \ + SUN4I_HDMI_DDC_INT_STATUS_ILLEGAL_FIFO_OPERATION | \ + SUN4I_HDMI_DDC_INT_STATUS_DDC_RX_FIFO_UNDERFLOW | \ + SUN4I_HDMI_DDC_INT_STATUS_DDC_TX_FIFO_OVERFLOW | \ + SUN4I_HDMI_DDC_INT_STATUS_ARBITRATION_ERROR | \ + SUN4I_HDMI_DDC_INT_STATUS_ACK_ERROR | \ + SUN4I_HDMI_DDC_INT_STATUS_BUS_ERROR \ +) + +#define SUN4I_HDMI_DDC_FIFO_CTRL_REG 0x10 +#define SUN4I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(31) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES(n) (((n) & 0xf) << 4) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MASK GENMASK(7, 4) +#define SUN4I_HDMI_DDC_FIFO_CTRL_RX_THRES_MAX (BIT(4) - 1) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES(n) ((n) & 0xf) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MASK GENMASK(3, 0) +#define SUN4I_HDMI_DDC_FIFO_CTRL_TX_THRES_MAX (BIT(4) - 1) + +#define SUN4I_HDMI_DDC_FIFO_DATA_REG 0x18 + +#define SUN4I_HDMI_DDC_BYTE_COUNT_REG 0x1c +#define SUN4I_HDMI_DDC_BYTE_COUNT_MAX (BIT(10) - 1) + +#define SUN4I_HDMI_DDC_CMD_REG 0x20 +#define SUN4I_HDMI_DDC_CMD_EXPLICIT_EDDC_READ 6 +#define SUN4I_HDMI_DDC_CMD_IMPLICIT_READ 5 +#define SUN4I_HDMI_DDC_CMD_IMPLICIT_WRITE 3 + +#define SUN4I_HDMI_DDC_CLK_REG 0x28 +#define SUN4I_HDMI_DDC_CLK_M(m) (((m) & 0x7) << 3) +#define SUN4I_HDMI_DDC_CLK_N(n) ((n) & 0x7) + +#define SUN4I_HDMI_DDC_LINE_CTRL_REG 0x40 +#define SUN4I_HDMI_DDC_LINE_CTRL_SDA_ENABLE BIT(9) +#define SUN4I_HDMI_DDC_LINE_CTRL_SCL_ENABLE BIT(8) + +#define SUN4I_HDMI_DDC_FIFO_SIZE 16 + +/* A31 specific */ +#define SUN6I_HDMI_DDC_CTRL_REG 0x00 +#define SUN6I_HDMI_DDC_CTRL_RESET BIT(31) +#define SUN6I_HDMI_DDC_CTRL_START_CMD BIT(27) +#define SUN6I_HDMI_DDC_CTRL_SDA_ENABLE BIT(6) +#define SUN6I_HDMI_DDC_CTRL_SCL_ENABLE BIT(4) +#define SUN6I_HDMI_DDC_CTRL_ENABLE BIT(0) + +#define SUN6I_HDMI_DDC_CMD_REG 0x08 +#define SUN6I_HDMI_DDC_CMD_BYTE_COUNT(count) ((count) << 16) +/* command types in lower 3 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_ADDR_REG 0x0c +#define SUN6I_HDMI_DDC_ADDR_SEGMENT(seg) (((seg) & 0xff) << 24) +#define SUN6I_HDMI_DDC_ADDR_EDDC(addr) (((addr) & 0xff) << 16) +#define SUN6I_HDMI_DDC_ADDR_OFFSET(off) (((off) & 0xff) << 8) +#define SUN6I_HDMI_DDC_ADDR_SLAVE(addr) (((addr) & 0xff) << 1) + +#define SUN6I_HDMI_DDC_INT_STATUS_REG 0x14 +#define SUN6I_HDMI_DDC_INT_STATUS_TIMEOUT BIT(8) +/* lower 8 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_FIFO_CTRL_REG 0x18 +#define SUN6I_HDMI_DDC_FIFO_CTRL_CLEAR BIT(15) +/* lower 9 bits are the same as sun4i */ + +#define SUN6I_HDMI_DDC_CLK_REG 0x20 +/* DDC CLK bit fields are the same, but the formula is not */ + +#define SUN6I_HDMI_DDC_FIFO_DATA_REG 0x80 + +struct sun4i_hdmi_i2c_variant { + bool has_legacy_dt; + const char *ddc_parent_clk_name; + struct reg_field ddc_clk_reg; + u8 ddc_clk_pre_divider; + u8 ddc_clk_m_offset; + + /* Register fields for I2C adapter */ + struct reg_field field_ddc_en; + struct reg_field field_ddc_start; + struct reg_field field_ddc_reset; + struct reg_field field_ddc_addr_reg; + struct reg_field field_ddc_slave_addr; + struct reg_field field_ddc_int_mask; + struct reg_field field_ddc_int_status; + struct reg_field field_ddc_fifo_clear; + struct reg_field field_ddc_fifo_rx_thres; + struct reg_field field_ddc_fifo_tx_thres; + struct reg_field field_ddc_byte_count; + struct reg_field field_ddc_cmd; + struct reg_field field_ddc_sda_en; + struct reg_field field_ddc_sck_en; + + /* DDC FIFO register offset */ + u32 ddc_fifo_reg; + + /* + * DDC FIFO threshold boundary conditions + * + * This is used to cope with the threshold boundary condition + * being slightly different on sun5i and sun6i. + * + * On sun5i the threshold is exclusive, i.e. does not include, + * the value of the threshold. ( > for RX; < for TX ) + * On sun6i the threshold is inclusive, i.e. includes, the + * value of the threshold. ( >= for RX; <= for TX ) + */ + bool ddc_fifo_thres_incl; + + bool ddc_fifo_has_dir; +}; + +struct sun4i_hdmi_i2c_drv { + struct device *dev; + + void __iomem *base; + struct regmap *regmap; + + struct clk *ddc_parent_clk; + struct clk *ddc_clk; + uint32_t clock_freq; + + struct i2c_adapter adap; + + struct regmap_field *field_ddc_en; + struct regmap_field *field_ddc_start; + struct regmap_field *field_ddc_reset; + struct regmap_field *field_ddc_addr_reg; + struct regmap_field *field_ddc_slave_addr; + struct regmap_field *field_ddc_int_mask; + struct regmap_field *field_ddc_int_status; + struct regmap_field *field_ddc_fifo_clear; + struct regmap_field *field_ddc_fifo_rx_thres; + struct regmap_field *field_ddc_fifo_tx_thres; + struct regmap_field *field_ddc_byte_count; + struct regmap_field *field_ddc_cmd; + struct regmap_field *field_ddc_sda_en; + struct regmap_field *field_ddc_sck_en; + + const struct sun4i_hdmi_i2c_variant *variant; +}; + +struct sun4i_hdmi_i2c_drv +*sun4i_hdmi_i2c_init(struct device *dev, void __iomem *base, + const struct of_device_id *of_id_table, + const struct regmap_config *regmap_config); + +void sun4i_hdmi_i2c_fini(struct sun4i_hdmi_i2c_drv *drv); + +struct sun4i_hdmi_i2c_drv *sun4i_hdmi_i2c_setup(struct device *dev, + void __iomem *base); + +#endif diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c index dc332ea56f6c75..2bfe6b0b3e9995 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c @@ -10,7 +10,13 @@ * the License, or (at your option) any later version. */ +#include #include +#include +#include +#include +#include +#include #include "sun4i_hdmi.h" @@ -21,6 +27,15 @@ struct sun4i_tmds { u8 div_offset; }; +struct sun4i_tmds_ddc { + struct device *dev; + void __iomem *base; + + struct clk_hw hw; + + spinlock_t lock; +}; + static inline struct sun4i_tmds *hw_to_tmds(struct clk_hw *hw) { return container_of(hw, struct sun4i_tmds, hw); @@ -218,7 +233,10 @@ int sun4i_tmds_create(struct sun4i_hdmi *hdmi) if (!tmds) return -ENOMEM; - init.name = "hdmi-tmds"; + if (!hdmi->tmds_clk_name) + return -ENODEV; + + init.name = hdmi->tmds_clk_name; init.ops = &sun4i_tmds_ops; init.parent_names = parents; init.num_parents = 2; From beb2e137aee774a6d11ca0fe7a2c0146aba62d9b Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 6 Feb 2018 11:16:25 +0100 Subject: [PATCH 53/57] fix whitespace (kconfig) Signed-off-by: Olliver Schinagl --- drivers/gpu/drm/sun4i/Kconfig | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig index c879b11e55cd0b..3eff346b593c79 100644 --- a/drivers/gpu/drm/sun4i/Kconfig +++ b/drivers/gpu/drm/sun4i/Kconfig @@ -23,21 +23,19 @@ config DRM_SUN4I_HDMI controller. config I2C_SUN4I_HDMI - tristate "Allwinner A10 HDMI I2C Support" - depends on DRM_SUN4I_HDMI - default DRM_SUN4I_HDMI - select I2C - help - Choose this option if you have an Allwinner SoC with an I2C - enabled HDMI controller and want to enable the I2C channel. - + tristate "Allwinner A10 HDMI I2C Support" + depends on DRM_SUN4I_HDMI + select I2C + help + Choose this option if you have an Allwinner SoC with an I2C + enabled HDMI controller and want to enable the I2C channel. config DRM_SUN4I_HDMI_CEC bool "Allwinner A10 HDMI CEC Support" depends on DRM_SUN4I_HDMI select CEC_CORE select CEC_PIN - help + help Choose this option if you have an Allwinner SoC with an HDMI controller and want to use CEC. From 6d00bfccfb5746d97d4d12672e10d62f0de573ca Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 6 Feb 2018 11:17:00 +0100 Subject: [PATCH 54/57] fix hdmi freq divider Signed-off-by: Olliver Schinagl --- drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c index 62b7e240a87b73..c037a7428bc85e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_i2c.c @@ -23,7 +23,7 @@ static const struct sun4i_hdmi_i2c_variant sun4i_variant = { .has_legacy_dt = true, .ddc_parent_clk_name = NULL, .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), - .ddc_clk_pre_divider = 2, + .ddc_clk_pre_divider = 4, .ddc_clk_m_offset = 1, .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), From 795cc472cd45fa112ea238783211bb2d9a6dc031 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 6 Feb 2018 11:17:34 +0100 Subject: [PATCH 55/57] rtc: sun6i: add missing header Signed-off-by: Olliver Schinagl --- drivers/rtc/rtc-sun6i.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 3d2216ccd860c6..d941c66138df0d 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include From 14d61820792a62e73fc9455ae2ec0f8bef3aa403 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 6 Feb 2018 11:18:31 +0100 Subject: [PATCH 56/57] of_get_matching_data Signed-off-by: Olliver Schinagl --- drivers/of/base.c | 20 ++++++++++++++++++++ include/linux/of.h | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/drivers/of/base.c b/drivers/of/base.c index 260d33c0f26c9b..faef97767f7ab7 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1026,6 +1026,26 @@ const struct of_device_id *of_match_node(const struct of_device_id *matches, } EXPORT_SYMBOL(of_match_node); +/** + * of_get_match_data - Get the matching device_node data pointer + * @matches: array of of device match structures to search in + * @node: the of device structure to match against + * + * Low level utility function used to get the device matching data pointer. + */ +const void *of_get_match_data(const struct of_device_id *matches, + const struct device_node *node) +{ + const struct of_device_id *match; + + match = of_match_node(matches, node); + if (!match) + return NULL; + + return match->data; +} +EXPORT_SYMBOL(of_get_match_data); + /** * of_find_matching_node_and_match - Find a node based on an of_device_id * match table. diff --git a/include/linux/of.h b/include/linux/of.h index cfc34117fc9203..992c3f15c64d0a 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -353,6 +353,8 @@ extern int of_n_addr_cells(struct device_node *np); extern int of_n_size_cells(struct device_node *np); extern const struct of_device_id *of_match_node( const struct of_device_id *matches, const struct device_node *node); +extern const void *of_get_match_data(const struct of_device_id *matches, + const struct device_node *node); extern int of_modalias_node(struct device_node *node, char *modalias, int len); extern void of_print_phandle_args(const char *msg, const struct of_phandle_args *args); extern struct device_node *of_parse_phandle(const struct device_node *np, @@ -862,6 +864,12 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag { } +static inline const void *of_get_match_data(const struct of_device_id *match, + const struct device_node *node); +{ + return NULL; +} + #define of_match_ptr(_ptr) NULL #define of_match_node(_matches, _node) NULL #endif /* CONFIG_OF */ From ac68ebb4407003615964a5c13b121a4ef093fe8e Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Tue, 6 Feb 2018 11:19:02 +0100 Subject: [PATCH 57/57] of_reset_control_get_exclusive helper Signed-off-by: Olliver Schinagl --- include/linux/reset.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/linux/reset.h b/include/linux/reset.h index 56463f37f3e67e..8e7ec032e89eb1 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -188,6 +188,23 @@ static inline struct reset_control *of_reset_control_get_exclusive( return __of_reset_control_get(node, id, 0, false, false); } +/** + * of_reset_control_get_exclusive - Lookup and obtain an exclusive reference + * to an optional reset controller. + * @node: device to be reset by the controller + * @id: reset line name + * + * Returns a struct reset_control, NULL when not found or IS_ERR() condition + * containing errno. + * + * Use of id names is optional. + */ +static inline struct reset_control *of_reset_control_get_optional_exclusive( + struct device_node *node, const char *id) +{ + return __of_reset_control_get(node, id, 0, false, true); +} + /** * of_reset_control_get_shared - Lookup and obtain an shared reference * to a reset controller.