Skip to content

Commit 260249f

Browse files
aford173bebarino
authored andcommitted
clk: vc5: Enable addition output configurations of the Versaclock
The existing driver is expecting the Versaclock to be pre-programmed, and only sets the output frequency. Unfortunately, not all devices are pre-programmed, and the Versaclock chip has more options beyond just the frequency. This patch enables the following additional features: - Programmable voltage: 1.8V, 2.5V, or 3.3V​ - Slew Percentage of normal: 85%, 90%, or 100% - Output Type: LVPECL, CMOS, HCSL, or LVDS Signed-off-by: Adam Ford <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Stephen Boyd <[email protected]>
1 parent 34662f6 commit 260249f

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

drivers/clk/clk-versaclock5.c

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <linux/regmap.h>
2525
#include <linux/slab.h>
2626

27+
#include <dt-bindings/clk/versaclock.h>
28+
2729
/* VersaClock5 registers */
2830
#define VC5_OTP_CONTROL 0x00
2931

@@ -89,6 +91,28 @@
8991

9092
/* Clock control register for clock 1,2 */
9193
#define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n))
94+
#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT 5
95+
#define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT)
96+
97+
#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL (VC5_LVPECL)
98+
#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS (VC5_CMOS)
99+
#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33 (VC5_HCSL33)
100+
#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS (VC5_LVDS)
101+
#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2 (VC5_CMOS2)
102+
#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD (VC5_CMOSD)
103+
#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25 (VC5_HCSL25)
104+
105+
#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT 3
106+
#define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
107+
#define VC5_CLK_OUTPUT_CFG0_PWR_18 (0<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
108+
#define VC5_CLK_OUTPUT_CFG0_PWR_25 (2<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
109+
#define VC5_CLK_OUTPUT_CFG0_PWR_33 (3<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT)
110+
#define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT 0
111+
#define VC5_CLK_OUTPUT_CFG0_SLEW_MASK GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
112+
#define VC5_CLK_OUTPUT_CFG0_SLEW_80 (0<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
113+
#define VC5_CLK_OUTPUT_CFG0_SLEW_85 (1<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
114+
#define VC5_CLK_OUTPUT_CFG0_SLEW_90 (2<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
115+
#define VC5_CLK_OUTPUT_CFG0_SLEW_100 (3<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT)
92116
#define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0)
93117

94118
#define VC5_CLK_OE_SHDN 0x68
@@ -143,6 +167,8 @@ struct vc5_hw_data {
143167
u32 div_int;
144168
u32 div_frc;
145169
unsigned int num;
170+
unsigned int clk_output_cfg0;
171+
unsigned int clk_output_cfg0_mask;
146172
};
147173

148174
struct vc5_driver_data {
@@ -567,6 +593,17 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
567593
regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
568594
VC5_CLK_OUTPUT_CFG1_EN_CLKBUF,
569595
VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
596+
if (hwdata->clk_output_cfg0_mask) {
597+
dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n",
598+
hwdata->num, hwdata->clk_output_cfg0_mask,
599+
hwdata->clk_output_cfg0);
600+
601+
regmap_update_bits(vc5->regmap,
602+
VC5_CLK_OUTPUT_CFG(hwdata->num, 0),
603+
hwdata->clk_output_cfg0_mask,
604+
hwdata->clk_output_cfg0);
605+
}
606+
570607
return 0;
571608
}
572609

@@ -666,6 +703,120 @@ static int vc5_map_index_to_output(const enum vc5_model model,
666703
}
667704
}
668705

706+
static int vc5_update_mode(struct device_node *np_output,
707+
struct vc5_hw_data *clk_out)
708+
{
709+
u32 value;
710+
711+
if (!of_property_read_u32(np_output, "idt,mode", &value)) {
712+
clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK;
713+
switch (value) {
714+
case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL:
715+
case VC5_CLK_OUTPUT_CFG0_CFG_CMOS:
716+
case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33:
717+
case VC5_CLK_OUTPUT_CFG0_CFG_LVDS:
718+
case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2:
719+
case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD:
720+
case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25:
721+
clk_out->clk_output_cfg0 |=
722+
value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT;
723+
break;
724+
default:
725+
return -EINVAL;
726+
}
727+
}
728+
return 0;
729+
}
730+
731+
static int vc5_update_power(struct device_node *np_output,
732+
struct vc5_hw_data *clk_out)
733+
{
734+
u32 value;
735+
736+
if (!of_property_read_u32(np_output,
737+
"idt,voltage-microvolts", &value)) {
738+
clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK;
739+
switch (value) {
740+
case 1800000:
741+
clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18;
742+
break;
743+
case 2500000:
744+
clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25;
745+
break;
746+
case 3300000:
747+
clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33;
748+
break;
749+
default:
750+
return -EINVAL;
751+
}
752+
}
753+
return 0;
754+
}
755+
756+
static int vc5_update_slew(struct device_node *np_output,
757+
struct vc5_hw_data *clk_out)
758+
{
759+
u32 value;
760+
761+
if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) {
762+
clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK;
763+
switch (value) {
764+
case 80:
765+
clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80;
766+
break;
767+
case 85:
768+
clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85;
769+
break;
770+
case 90:
771+
clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90;
772+
break;
773+
case 100:
774+
clk_out->clk_output_cfg0 |=
775+
VC5_CLK_OUTPUT_CFG0_SLEW_100;
776+
break;
777+
default:
778+
return -EINVAL;
779+
}
780+
}
781+
return 0;
782+
}
783+
784+
static int vc5_get_output_config(struct i2c_client *client,
785+
struct vc5_hw_data *clk_out)
786+
{
787+
struct device_node *np_output;
788+
char *child_name;
789+
int ret = 0;
790+
791+
child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1);
792+
np_output = of_get_child_by_name(client->dev.of_node, child_name);
793+
kfree(child_name);
794+
if (!np_output)
795+
goto output_done;
796+
797+
ret = vc5_update_mode(np_output, clk_out);
798+
if (ret)
799+
goto output_error;
800+
801+
ret = vc5_update_power(np_output, clk_out);
802+
if (ret)
803+
goto output_error;
804+
805+
ret = vc5_update_slew(np_output, clk_out);
806+
807+
output_error:
808+
if (ret) {
809+
dev_err(&client->dev,
810+
"Invalid clock output configuration OUT%d\n",
811+
clk_out->num + 1);
812+
}
813+
814+
of_node_put(np_output);
815+
816+
output_done:
817+
return ret;
818+
}
819+
669820
static const struct of_device_id clk_vc5_of_match[];
670821

671822
static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id)
@@ -863,6 +1014,11 @@ static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id)
8631014
init.name);
8641015
goto err_clk;
8651016
}
1017+
1018+
/* Fetch Clock Output configuration from DT (if specified) */
1019+
ret = vc5_get_output_config(client, &vc5->clk_out[n]);
1020+
if (ret)
1021+
goto err_clk;
8661022
}
8671023

8681024
ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5);

0 commit comments

Comments
 (0)