Commit b4dc4e13 authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

media: atomisp: add support for different PMIC configurations

This patch required lots of research and work. The existing
atomisp driver at staging assumed that all Intel PMIC would
be using regulators, but upstream didn't follow it. Instead,
the intel_pmic.c driver added a hack, instead of using i2c_transfer,
it writes I2C values directly via regmapped registers.

Oh, well... At least, it provided a common API for doing that.

The PMIC settings used here came from the driver at the
yocto Aero distribution:

	https://download.01.org/aero/deb/pool/main/l/linux-4.4.76-aero-1.3/

The logic itself was re-written, in order to use the I2C address
detected by the probing part.
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 0741bf66
...@@ -25,13 +25,39 @@ enum clock_rate { ...@@ -25,13 +25,39 @@ enum clock_rate {
#define CLK_RATE_19_2MHZ 19200000 #define CLK_RATE_19_2MHZ 19200000
#define CLK_RATE_25_0MHZ 25000000 #define CLK_RATE_25_0MHZ 25000000
/* X-Powers AXP288 register set */
#define ALDO1_SEL_REG 0x28
#define ALDO1_CTRL3_REG 0x13
#define ALDO1_2P8V 0x16
#define ALDO1_CTRL3_SHIFT 0x05
#define ELDO_CTRL_REG 0x12
#define ELDO1_SEL_REG 0x19 #define ELDO1_SEL_REG 0x19
#define ELDO1_1P8V 0x16 #define ELDO1_1P8V 0x16
#define ELDO1_CTRL_SHIFT 0x00 #define ELDO1_CTRL_SHIFT 0x00
#define ELDO2_SEL_REG 0x1a #define ELDO2_SEL_REG 0x1a
#define ELDO2_1P8V 0x16 #define ELDO2_1P8V 0x16
#define ELDO2_CTRL_SHIFT 0x01 #define ELDO2_CTRL_SHIFT 0x01
/* TI SND9039 PMIC register set */
#define LDO9_REG 0x49
#define LDO10_REG 0x4a
#define LDO11_REG 0x4b
#define LDO_2P8V_ON 0x2f /* 0x2e selects 2.85V ... */
#define LDO_2P8V_OFF 0x2e /* ... bottom bit is "enabled" */
#define LDO_1P8V_ON 0x59 /* 0x58 selects 1.80V ... */
#define LDO_1P8V_OFF 0x58 /* ... bottom bit is "enabled" */
/* CRYSTAL COVE PMIC register set */
#define CRYSTAL_1P8V_REG 0x57
#define CRYSTAL_2P8V_REG 0x5d
#define CRYSTAL_ON 0x63
#define CRYSTAL_OFF 0x62
struct gmin_subdev { struct gmin_subdev {
struct v4l2_subdev *subdev; struct v4l2_subdev *subdev;
int clock_num; int clock_num;
...@@ -52,6 +78,12 @@ struct gmin_subdev { ...@@ -52,6 +78,12 @@ struct gmin_subdev {
bool v2p8_on; bool v2p8_on;
bool v1p2_on; bool v1p2_on;
bool v2p8_vcm_on; bool v2p8_vcm_on;
u8 pwm_i2c_addr;
/* For PMIC AXP */
int eldo1_sel_reg, eldo1_1p8v, eldo1_ctrl_shift;
int eldo2_sel_reg, eldo2_1p8v, eldo2_ctrl_shift;
}; };
static struct gmin_subdev gmin_subdevs[MAX_SUBDEVS]; static struct gmin_subdev gmin_subdevs[MAX_SUBDEVS];
...@@ -61,6 +93,8 @@ static struct gmin_subdev gmin_subdevs[MAX_SUBDEVS]; ...@@ -61,6 +93,8 @@ static struct gmin_subdev gmin_subdevs[MAX_SUBDEVS];
#define PMIC_ACPI_TI "INT33F5:00" /* Dollar Cove TI PMIC */ #define PMIC_ACPI_TI "INT33F5:00" /* Dollar Cove TI PMIC */
#define PMIC_ACPI_CRYSTALCOVE "INT33FD:00" /* Crystal Cove PMIC */ #define PMIC_ACPI_CRYSTALCOVE "INT33FD:00" /* Crystal Cove PMIC */
#define PMIC_PLATFORM_TI "intel_soc_pmic_chtdc_ti"
static enum { static enum {
PMIC_UNSET = 0, PMIC_UNSET = 0,
PMIC_REGULATOR, PMIC_REGULATOR,
...@@ -358,15 +392,9 @@ static const struct dmi_system_id gmin_vars[] = { ...@@ -358,15 +392,9 @@ static const struct dmi_system_id gmin_vars[] = {
#define GMIN_PMC_CLK_NAME 14 /* "pmc_plt_clk_[0..5]" */ #define GMIN_PMC_CLK_NAME 14 /* "pmc_plt_clk_[0..5]" */
static char gmin_pmc_clk_name[GMIN_PMC_CLK_NAME]; static char gmin_pmc_clk_name[GMIN_PMC_CLK_NAME];
struct gmin_match_name { static int gmin_i2c_match_one(struct device *dev, const void *data)
const char *name;
struct device *dev;
};
static int gmin_match_one(struct device *dev, void *data)
{ {
struct gmin_match_name *match = data; const char *name = data;
const char *name = match->name;
struct i2c_client *client; struct i2c_client *client;
if (dev->type != &i2c_client_type) if (dev->type != &i2c_client_type)
...@@ -374,38 +402,61 @@ static int gmin_match_one(struct device *dev, void *data) ...@@ -374,38 +402,61 @@ static int gmin_match_one(struct device *dev, void *data)
client = to_i2c_client(dev); client = to_i2c_client(dev);
dev_info(match->dev, "found '%s' at address 0x%02x, adapter %d\n",
client->name, client->addr, client->adapter->nr);
return (!strcmp(name, client->name)); return (!strcmp(name, client->name));
} }
static bool gmin_i2c_dev_exists(struct device *dev, char *name) static struct i2c_client *gmin_i2c_dev_exists(struct device *dev, char *name,
struct i2c_client **client)
{ {
struct gmin_match_name match; struct device *d;
bool found;
int ret = 0; while ((d = bus_find_device(&i2c_bus_type, NULL, name,
gmin_i2c_match_one))) {
*client = to_i2c_client(d);
dev_dbg(dev, "found '%s' at address 0x%02x, adapter %d\n",
(*client)->name, (*client)->addr,
(*client)->adapter->nr);
return *client;
}
return NULL;
}
static int gmin_i2c_write(struct device *dev, u16 i2c_addr, u8 reg,
u32 value, u32 mask)
{
int ret;
match.dev = dev; /*
match.name = name; * FIXME: Right now, the intel_pmic driver just write values
* directly at the regmap, instead of properly implementing
* i2c_transfer() mechanism. Let's use the same interface here,
* as otherwise we may face issues.
*/
ret = i2c_for_each_dev(&match, gmin_match_one); dev_dbg(dev,
"I2C write, addr: 0x%02x, reg: 0x%02x, value: 0x%02x, mask: 0x%02x\n",
i2c_addr, reg, value, mask);
found = !!ret; ret = intel_soc_pmic_exec_mipi_pmic_seq_element(i2c_addr, reg,
value, mask);
if (found) if (ret == -EOPNOTSUPP) {
dev_info(dev, "%s found on I2C\n", name); dev_err(dev,
else "ACPI didn't mapped the OpRegion needed to access I2C address 0x%02x.\n"
dev_info(dev, "%s not found on I2C\n", name); "Need to compile the Kernel using CONFIG_*_PMIC_OPREGION settings\n",
i2c_addr);
return ret;
}
return found; return ret;
} }
static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
{ {
int i, ret; int i, ret;
struct device *dev; struct device *dev;
struct i2c_client *client = v4l2_get_subdevdata(subdev); struct i2c_client *power = NULL, *client = v4l2_get_subdevdata(subdev);
if (!client) if (!client)
return NULL; return NULL;
...@@ -413,11 +464,11 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) ...@@ -413,11 +464,11 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
dev = &client->dev; dev = &client->dev;
if (!pmic_id) { if (!pmic_id) {
if (gmin_i2c_dev_exists(dev, PMIC_ACPI_TI)) if (gmin_i2c_dev_exists(dev, PMIC_ACPI_TI, &power))
pmic_id = PMIC_TI; pmic_id = PMIC_TI;
else if (gmin_i2c_dev_exists(dev, PMIC_ACPI_AXP)) else if (gmin_i2c_dev_exists(dev, PMIC_ACPI_AXP, &power))
pmic_id = PMIC_AXP; pmic_id = PMIC_AXP;
else if (gmin_i2c_dev_exists(dev, PMIC_ACPI_CRYSTALCOVE)) else if (gmin_i2c_dev_exists(dev, PMIC_ACPI_CRYSTALCOVE, &power))
pmic_id = PMIC_CRYSTALCOVE; pmic_id = PMIC_CRYSTALCOVE;
else else
pmic_id = PMIC_REGULATOR; pmic_id = PMIC_REGULATOR;
...@@ -428,9 +479,16 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) ...@@ -428,9 +479,16 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
if (i >= MAX_SUBDEVS) if (i >= MAX_SUBDEVS)
return NULL; return NULL;
if (power) {
gmin_subdevs[i].pwm_i2c_addr = power->addr;
dev_info(dev, dev_info(dev,
"gmin: power management provided via %s\n", "gmin: power management provided via %s (i2c addr 0x%02x)\n",
pmic_name[pmic_id], power->addr);
} else {
dev_info(dev, "gmin: power management provided via %s\n",
pmic_name[pmic_id]); pmic_name[pmic_id]);
}
gmin_subdevs[i].subdev = subdev; gmin_subdevs[i].subdev = subdev;
gmin_subdevs[i].clock_num = gmin_get_var_int(dev, false, "CamClk", 0); gmin_subdevs[i].clock_num = gmin_get_var_int(dev, false, "CamClk", 0);
...@@ -479,26 +537,14 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) ...@@ -479,26 +537,14 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
if (IS_ERR(gmin_subdevs[i].gpio1)) if (IS_ERR(gmin_subdevs[i].gpio1))
gmin_subdevs[i].gpio1 = NULL; gmin_subdevs[i].gpio1 = NULL;
if (pmic_id == PMIC_REGULATOR) { switch (pmic_id) {
/* Those regulators may have different names depending on the BIOS */ case PMIC_REGULATOR:
gmin_subdevs[i].v1p8_reg = regulator_get_optional(dev, "V1P8SX"); gmin_subdevs[i].v1p8_reg = regulator_get(dev, "V1P8SX");
gmin_subdevs[i].v2p8_reg = regulator_get_optional(dev, "V2P8SX"); gmin_subdevs[i].v2p8_reg = regulator_get(dev, "V2P8SX");
gmin_subdevs[i].v1p2_reg = regulator_get(dev, "V1P2A"); gmin_subdevs[i].v1p2_reg = regulator_get(dev, "V1P2A");
gmin_subdevs[i].v2p8_vcm_reg = regulator_get(dev, "VPROG4B"); gmin_subdevs[i].v2p8_vcm_reg = regulator_get(dev, "VPROG4B");
/*
* Based on DTST dumps on newer Atom E3800 devices, it seems that
* the regulators data now have new names.
*/
if (IS_ERR(gmin_subdevs[i].v1p8_reg))
gmin_subdevs[i].v1p8_reg = regulator_get(dev, "Regulator1p8v");
if (IS_ERR(gmin_subdevs[i].v2p8_reg))
gmin_subdevs[i].v2p8_reg = regulator_get(dev, "Regulator2p8v");
/* Note: ideally we would initialize v[12]p8_on to the /* Note: ideally we would initialize v[12]p8_on to the
* output of regulator_is_enabled(), but sadly that * output of regulator_is_enabled(), but sadly that
* API is broken with the current drivers, returning * API is broken with the current drivers, returning
...@@ -506,6 +552,32 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev) ...@@ -506,6 +552,32 @@ static struct gmin_subdev *gmin_subdev_add(struct v4l2_subdev *subdev)
* "unbalanced disable" WARNing if we try to disable * "unbalanced disable" WARNing if we try to disable
* it. * it.
*/ */
break;
case PMIC_AXP:
gmin_subdevs[i].eldo1_1p8v = gmin_get_var_int(dev, false,
"eldo1_1p8v",
ELDO1_1P8V);
gmin_subdevs[i].eldo1_sel_reg = gmin_get_var_int(dev, false,
"eldo1_sel_reg",
ELDO1_SEL_REG);
gmin_subdevs[i].eldo1_ctrl_shift = gmin_get_var_int(dev, false,
"eldo1_ctrl_shift",
ELDO1_CTRL_SHIFT);
gmin_subdevs[i].eldo2_1p8v = gmin_get_var_int(dev, false,
"eldo2_1p8v",
ELDO2_1P8V);
gmin_subdevs[i].eldo2_sel_reg = gmin_get_var_int(dev, false,
"eldo2_sel_reg",
ELDO2_SEL_REG);
gmin_subdevs[i].eldo2_ctrl_shift = gmin_get_var_int(dev, false,
"eldo2_ctrl_shift",
ELDO2_CTRL_SHIFT);
gmin_subdevs[i].pwm_i2c_addr = power->addr;
break;
default:
break;
} }
return &gmin_subdevs[i]; return &gmin_subdevs[i];
...@@ -521,6 +593,64 @@ static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev) ...@@ -521,6 +593,64 @@ static struct gmin_subdev *find_gmin_subdev(struct v4l2_subdev *subdev)
return gmin_subdev_add(subdev); return gmin_subdev_add(subdev);
} }
static int axp_regulator_set(struct device *dev, struct gmin_subdev *gs,
int sel_reg, u8 setting,
int ctrl_reg, int shift, bool on)
{
int ret;
int val;
ret = gmin_i2c_write(dev, gs->pwm_i2c_addr, sel_reg, setting, 0xff);
if (ret)
return ret;
val = on ? 1 << shift : 0;
ret = gmin_i2c_write(dev, gs->pwm_i2c_addr, sel_reg, val, 1 << shift);
if (ret)
return ret;
return 0;
}
static int axp_v1p8_on(struct device *dev, struct gmin_subdev *gs)
{
int ret;
ret = axp_regulator_set(dev, gs, gs->eldo2_sel_reg, gs->eldo2_1p8v,
ELDO_CTRL_REG, gs->eldo2_ctrl_shift, true);
if (ret)
return ret;
/*
* This sleep comes out of the gc2235 driver, which is the
* only one I currently see that wants to set both 1.8v rails.
*/
usleep_range(110, 150);
ret = axp_regulator_set(dev, gs, gs->eldo1_sel_reg, gs->eldo1_1p8v,
ELDO_CTRL_REG, gs->eldo1_ctrl_shift, true);
if (ret)
return ret;
ret = axp_regulator_set(dev, gs, gs->eldo2_sel_reg, gs->eldo2_1p8v,
ELDO_CTRL_REG, gs->eldo2_ctrl_shift, false);
return ret;
}
static int axp_v1p8_off(struct device *dev, struct gmin_subdev *gs)
{
int ret;
ret = axp_regulator_set(dev, gs, gs->eldo1_sel_reg, gs->eldo1_1p8v,
ELDO_CTRL_REG, gs->eldo1_ctrl_shift, false);
if (ret)
return ret;
ret = axp_regulator_set(dev, gs, gs->eldo2_sel_reg, gs->eldo2_1p8v,
ELDO_CTRL_REG, gs->eldo2_ctrl_shift, false);
return ret;
}
static int gmin_gpio0_ctrl(struct v4l2_subdev *subdev, int on) static int gmin_gpio0_ctrl(struct v4l2_subdev *subdev, int on)
{ {
struct gmin_subdev *gs = find_gmin_subdev(subdev); struct gmin_subdev *gs = find_gmin_subdev(subdev);
...@@ -551,6 +681,7 @@ static int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on) ...@@ -551,6 +681,7 @@ static int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on)
return 0; return 0;
gs->v1p2_on = on; gs->v1p2_on = on;
/* use regulator for PMIC */
if (gs->v1p2_reg) { if (gs->v1p2_reg) {
if (on) if (on)
return regulator_enable(gs->v1p2_reg); return regulator_enable(gs->v1p2_reg);
...@@ -558,7 +689,7 @@ static int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on) ...@@ -558,7 +689,7 @@ static int gmin_v1p2_ctrl(struct v4l2_subdev *subdev, int on)
return regulator_disable(gs->v1p2_reg); return regulator_disable(gs->v1p2_reg);
} }
/*TODO:v1p2 needs to extend to other PMICs*/ /* TODO:v1p2 may need to extend to other PMICs */
return -EINVAL; return -EINVAL;
} }
...@@ -569,6 +700,7 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) ...@@ -569,6 +700,7 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on)
int ret; int ret;
struct device *dev; struct device *dev;
struct i2c_client *client = v4l2_get_subdevdata(subdev); struct i2c_client *client = v4l2_get_subdevdata(subdev);
int value;
dev = &client->dev; dev = &client->dev;
...@@ -601,6 +733,27 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on) ...@@ -601,6 +733,27 @@ static int gmin_v1p8_ctrl(struct v4l2_subdev *subdev, int on)
return regulator_disable(gs->v1p8_reg); return regulator_disable(gs->v1p8_reg);
} }
switch (pmic_id) {
case PMIC_AXP:
if (on)
return axp_v1p8_on(subdev->dev, gs);
else
return axp_v1p8_off(subdev->dev, gs);
case PMIC_TI:
value = on ? LDO_1P8V_ON : LDO_1P8V_OFF;
return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
LDO10_REG, value, 0xff);
case PMIC_CRYSTALCOVE:
value = on ? CRYSTAL_ON : CRYSTAL_OFF;
return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
CRYSTAL_1P8V_REG, value, 0xff);
default:
dev_err(subdev->dev, "Couldn't set power mode for v1p2\n");
}
return -EINVAL; return -EINVAL;
} }
...@@ -610,6 +763,7 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) ...@@ -610,6 +763,7 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
int ret; int ret;
struct device *dev; struct device *dev;
struct i2c_client *client = v4l2_get_subdevdata(subdev); struct i2c_client *client = v4l2_get_subdevdata(subdev);
int value;
dev = &client->dev; dev = &client->dev;
...@@ -642,6 +796,25 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on) ...@@ -642,6 +796,25 @@ static int gmin_v2p8_ctrl(struct v4l2_subdev *subdev, int on)
return regulator_disable(gs->v2p8_reg); return regulator_disable(gs->v2p8_reg);
} }
switch (pmic_id) {
case PMIC_AXP:
return axp_regulator_set(subdev->dev, gs, ALDO1_SEL_REG,
ALDO1_2P8V, ALDO1_CTRL3_REG,
ALDO1_CTRL3_SHIFT, on);
case PMIC_TI:
value = on ? LDO_2P8V_ON : LDO_2P8V_OFF;
return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
LDO9_REG, value, 0xff);
case PMIC_CRYSTALCOVE:
value = on ? CRYSTAL_ON : CRYSTAL_OFF;
return gmin_i2c_write(subdev->dev, gs->pwm_i2c_addr,
CRYSTAL_2P8V_REG, value, 0xff);
default:
dev_err(subdev->dev, "Couldn't set power mode for v1p2\n");
}
return -EINVAL; return -EINVAL;
} }
...@@ -655,7 +828,8 @@ static int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on) ...@@ -655,7 +828,8 @@ static int gmin_flisclk_ctrl(struct v4l2_subdev *subdev, int on)
return 0; return 0;
if (on) { if (on) {
ret = clk_set_rate(gs->pmc_clk, gs->clock_src ? CLK_RATE_19_2MHZ : CLK_RATE_25_0MHZ); ret = clk_set_rate(gs->pmc_clk,
gs->clock_src ? CLK_RATE_19_2MHZ : CLK_RATE_25_0MHZ);
if (ret) if (ret)
dev_err(&client->dev, "unable to set PMC rate %d\n", dev_err(&client->dev, "unable to set PMC rate %d\n",
...@@ -804,7 +978,8 @@ static int gmin_get_config_var(struct device *maindev, ...@@ -804,7 +978,8 @@ static int gmin_get_config_var(struct device *maindev,
id = dmi_first_match(gmin_vars); id = dmi_first_match(gmin_vars);
if (id) { if (id) {
dev_info(maindev, "Found DMI entry for '%s'\n", var8); dev_info(maindev, "Found DMI entry for '%s'\n", var8);
return gmin_get_hardcoded_var(id->driver_data, var8, out, out_len); return gmin_get_hardcoded_var(id->driver_data, var8, out,
out_len);
} }
/* Our variable names are ASCII by construction, but EFI names /* Our variable names are ASCII by construction, but EFI names
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment